跳到主要内容

lineinfile文件内容修改模块

[[toc]]

1. 概述

2. 参数

序号参数描述
1attributes string修改完文件后,文件的属性,类似使用chattr修改文件属性
2backrefs booleanstate=present一起使用,默认no。如果设置为yes,则可以使用反向引用(位置引用或命名引用),如果正则表达式regexp匹配上,则会填充反向引用。该参数会改变模块的操作,忽略掉insertbeforeinsertafter参数,如果正则表达式不能匹配到,则文件不会发生变化;如果正则匹配到了,则最后一个匹配将会被替换成line参数扩展的值。本参数与search_string参数互斥
3backup boolean是否备份原始文件,默认no
4create booleanstate=present一起使用,默认no。如果设置为yes,则当文件不存在时,会创建文件。默认情况下,如果文件不存在,则作业会失败。
5firstmatch booleaninsertbeforeinsertafter参数一起作用,默认no,如果设置为yes,那么会处理第一次的匹配项,而不是最后的匹配项
6group string文件修改后,文件的用户组名称,类似于chown命令
7insertafter stringstate=present一起使用,如果指定的话,那么会在最后一次匹配行的后一行插入行。
8insertbefore stringstate=present一起使用,如果指定的话,那么会在最后一次匹配行的前一行插入行。
9line string文件中需要插入/替换的行
10mode raw文件修改后的模式
11others stringfile模块支持的其他参数,都可以用在此参数中
12owner string文件修改后,对应的文件所有者,类似于命令chown
13path string待修改的文件路径
14regexp string需要在文件中查找的正则表达式。如果state=present,则当正则匹配成功时,只替换最后一次匹配的行。当state=absent时,匹配的行将会被移除。如果正则没有匹配上,那么行将会被添加到文件中。在修改行时,regexp通常应该匹配行的初始状态以及行替换后的状态,以确保幂等性。
15search_string string在文件中第一行匹配指定文本,不必匹配整行
16se*selinux相关配置,如selevel/serole/setype/seuser,本总结文档中忽略,详见官方文档
17state string状态,可选值:absent和present(默认)
18unsafe_writes boolean不安全写,默认no。此选项允许Ansible在原子操作失败时回退到更新文件系统对象的不安全方法
19validate string将更新的文件复制到最终目标之前要运行的验证命令

3. 官方示例

# NOTE: Before 2.3, option 'dest', 'destfile' or 'name' was used instead of 'path'
- name: Ensure SELinux is set to enforcing mode
ansible.builtin.lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: SELINUX=enforcing

- name: Make sure group wheel is not in the sudoers configuration
ansible.builtin.lineinfile:
path: /etc/sudoers
state: absent
regexp: '^%wheel'

- name: Replace a localhost entry with our own
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: '^127\.0\.0\.1'
line: 127.0.0.1 localhost
owner: root
group: root
mode: '0644'

- name: Replace a localhost entry searching for a literal string to avoid escaping
ansible.builtin.lineinfile:
path: /etc/hosts
search_string: '127.0.0.1'
line: 127.0.0.1 localhost
owner: root
group: root
mode: '0644'

- name: Ensure the default Apache port is 8080
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
insertafter: '^#Listen '
line: Listen 8080

- name: Ensure php extension matches new pattern
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
search_string: '<FilesMatch ".php[45]?$">'
insertafter: '^\t<Location \/>\n'
line: ' <FilesMatch ".php[34]?$">'

- name: Ensure we have our own comment added to /etc/services
ansible.builtin.lineinfile:
path: /etc/services
regexp: '^# port for http'
insertbefore: '^www.*80/tcp'
line: '# port for http by default'

- name: Add a line to a file if the file does not exist, without passing regexp
ansible.builtin.lineinfile:
path: /tmp/testfile
line: 192.168.1.99 foo.lab.net foo
create: yes

# NOTE: Yaml requires escaping backslashes in double quotes but not in single quotes
- name: Ensure the JBoss memory settings are exactly as needed
ansible.builtin.lineinfile:
path: /opt/jboss-as/bin/standalone.conf
regexp: '^(.*)Xms(\d+)m(.*)$'
line: '\1Xms${xms}m\3'
backrefs: yes

# NOTE: Fully quoted because of the ': ' on the line. See the Gotchas in the YAML docs.
- name: Validate the sudoers file before saving
ansible.builtin.lineinfile:
path: /etc/sudoers
state: present
regexp: '^%ADMIN ALL='
line: '%ADMIN ALL=(ALL) NOPASSWD: ALL'
validate: /usr/sbin/visudo -cf %s

# See https://docs.python.org/3/library/re.html for further details on syntax
- name: Use backrefs with alternative group syntax to avoid conflicts with variable values
ansible.builtin.lineinfile:
path: /tmp/config
regexp: ^(host=).*
line: \g<1>{{ hostname }}
backrefs: yes

4. 剧本的使用

4.1 SELINUX开启与关闭

查看当前node1节点的selinux配置情况:

[meizhaohui@node1 ~]$ getenforce
Disabled
[meizhaohui@node1 ~]$ grep '^SELINUX=' /etc/selinux/config
SELINUX=disabled

编写剧本文件lineinfile.yml:

- hosts: node1
tasks:
- name: Enable selinux
ansible.builtin.lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: SELINUX=enforcing
become: yes

检查剧本语法并执行:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Enable selinux] *************************************************************************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line replaced"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

此时,再次在节点上面查看selinux配置情况:

[meizhaohui@node1 ~]$ grep '^SELINUX=' /etc/selinux/config
SELINUX=enforcing

可以看到,配置已经被修改,修改成SELINUX=enforcing状态,说明SELINUX开启成功。

通常情况下,我们会关闭SELINUX,使用剧本将配置还原:

- hosts: node1
tasks:
- name: Disable selinux
ansible.builtin.lineinfile:
path: /etc/selinux/config
regexp: '^SELINUX='
line: SELINUX=disabled
become: yes

然后再执行剧本:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Disable selinux] *************************************************************************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line replaced"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

此时查看节点配置:

[meizhaohui@node1 ~]$ grep '^SELINUX=' /etc/selinux/config
SELINUX=disabled
[meizhaohui@node1 ~]$

可以看到SELINUX又变成关闭状态了。

4.2 显示远程主机IP

  • /etc/motd(message of to day:每日信息)文件常用于通告信息,如计划关机时间的警告等,登陆后的提示信息。
  • 通过该文件,我们可以用来显示远程主机的IP信息,便于我们确定远程主机IP。

编写剧本文件lineinfile.yml

- hosts: node1
tasks:
- name: Set motd info
ansible.builtin.lineinfile:
path: /etc/motd
regexp: '^IP:'
line: "IP:{{ ansible_default_ipv4['address'] }}"
become: yes

默认情况下,节点node1上面没有配置/etc/motd的内容。我们查看一下:

[meizhaohui@node1 ~]$ cat /etc/motd

可以看到没有内容。

检查剧本文件语法并执行:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Set motd info] ***************************************************************************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line added"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

此时,查看节点的配置文件/etc/motd文件内容:

[meizhaohui@node1 ~]$ cat /etc/motd
IP:192.168.12.1

此时,重新连接node1节点时,就可以看到远程主机IP信息了:

# 此处我配置了连接node1节点的快捷命令n1
[mzh@MacBookPro docs (master ✗)]$ n1
Last login: Tue Oct 11 20:09:19 2022 from 111.111.111.111
IP:192.168.12.1
[meizhaohui@node1 ~]$

可以看到,正常显示了远程主机的IP信息。

4.3 修改nginx监听端口并校验

我们编写修改nginx监听端口的剧本文件lineinfile.yml,当端口修改校验通过后,执行后面的任务,重新加载nginx配置:

- hosts: node1
tasks:
- name: Change nginx port from 80 to 8081
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^ listen 80;'
line: " listen 8081;"
validate: /usr/sbin/nginx -t -c %s
become: yes

- name: Reload Nginx server
ansible.builtin.command:
cmd: /usr/sbin/nginx -s reload
become: yes

在执行剧本前,我们先查看一下节点node1上nginx的配置,并看一下nginx的进程信息:

[root@node1 ~]# cd /etc/nginx/
[root@node1 nginx]# grep 'listen' nginx.conf
listen 80;
listen [::]:80;
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
[root@node1 nginx]# ps -ef|grep nginx
root 5673 1 0 20:31 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 7940 5673 0 20:47 ? 00:00:00 nginx: worker process
nginx 7941 5673 0 20:47 ? 00:00:00 nginx: worker process
root 8510 4951 0 20:51 pts/0 00:00:00 grep --color=auto nginx

::: warning 注意 在使用validate进行校验时,参数值中必须带%s信息。如果我们直接使用配置validate: /usr/sbin/nginx -t运行剧本,会提示"msg": "validate must contain %s: /usr/sbin/nginx -t"异常信息!!因此使用validate: /usr/sbin/nginx -t -c %s指定需要校验的配置文件。 :::

检查剧本文件语法并执行:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Change nginx port from 80 to 8081] *******************************************************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line replaced"}

TASK [Reload Nginx server] *********************************************************************************************
changed: [node1] => {"changed": true, "cmd": ["/usr/sbin/nginx", "-s", "reload"], "delta": "0:00:00.014303", "end": "2022-10-11 20:56:38.983474", "rc": 0, "start": "2022-10-11 20:56:38.969171", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

PLAY RECAP *************************************************************************************************************
node1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

此时,在节点上面查看配置信息和nginx进程信息:

[root@node1 nginx]# grep 'listen' nginx.conf
listen 8081;
listen [::]:80;
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
[root@node1 nginx]# ps -ef|grep nginx
root 5673 1 0 20:31 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 9916 5673 0 20:56 ? 00:00:00 nginx: worker process
nginx 9917 5673 0 20:56 ? 00:00:00 nginx: worker process
root 10113 4951 0 20:57 pts/0 00:00:00 grep --color=auto nginx

可以看到,配置文件更新了,并且进程也重启了!!说明我们配置的剧本生效了。

刚才,我们将监听端口从80端口修改成了8081端口,现在我们尝试还原,并将替换后值设置成一个不满足nginx要求的行信息。

- hosts: node1
tasks:
- name: Change nginx port from 8081 to 80 and with unknown directive Listen
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
regexp: '^ listen 8081;'
line: " Listen 80;"
validate: /usr/sbin/nginx -t -c %s
become: yes

- name: Reload Nginx server
ansible.builtin.command:
cmd: /usr/sbin/nginx -s reload
become: yes

检查剧本文件语法并执行:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Change nginx port from 8081 to 80 and with unknown directive Listen] *********************************************
fatal: [node1]: FAILED! => {"changed": false, "msg": "failed to validate: rc:1 error:nginx: [emerg] unknown directive \"Listen\" in /home/ansible/.ansible/tmp/ansible-tmp-1665498341.71-2122-273596304298361/tmppccYOi:39\nnginx: configuration file /home/ansible/.ansible/tmp/ansible-tmp-1665498341.71-2122-273596304298361/tmppccYOi test failed\n"}

PLAY RECAP *************************************************************************************************************
node1 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0

此时,可以看到,配置文件校验失败,提示unknown directive \"Listen\"未指指令Listen,因为我们故意将listen写成了Listen,所以nginx校验配置文件失败,校验失败后,并没有直接修改真正需要修改的配置文件/etc/nginx/nginx.conf,即通过validate参数的配置,可以确保我们修改后的配置文件校验正常。

我们将Listen修改为listen,然后再执行剧本:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Change nginx port from 8081 to 80 and with unknown directive Listen] *********************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line replaced"}

TASK [Reload Nginx server] *********************************************************************************************
changed: [node1] => {"changed": true, "cmd": ["/usr/sbin/nginx", "-s", "reload"], "delta": "0:00:00.013576", "end": "2022-10-11 22:36:23.692407", "rc": 0, "start": "2022-10-11 22:36:23.678831", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

PLAY RECAP *************************************************************************************************************
node1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

可以看到,校验成功,配置文件对应的行被替换掉。nginx服务也被重新加载。

4.4 其他示例

  • 默认情况下,本模块只会修改最后一次匹配的行。当指定firstmatch=yes参数时,则会修改第一次匹配的行。
  • 如果又指定insertbeforeinsertafter参数的话,则会在(最后一次或第一次)匹配行的前一行或后一行插入数据。

我们以修改nginx配置文件为例,查看nginx开头10行,并搜索Documentation关键字,可以看到,仅第2行和第3行包含Documentation关键字:

[root@node1 ~]# head /etc/nginx/nginx.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
[root@node1 ~]# grep -n 'Documentation' /etc/nginx/nginx.conf
2:# * Official English Documentation: http://nginx.org/en/docs/
3:# * Official Russian Documentation: http://nginx.org/ru/docs/

为了测试,我们需要尝试在``关键字行附近添加一个新行,新行内容是# * Download Nginx: http://nginx.org/en/download.html,即增加一个下载Nginx的链接地址。

4.4.1 修改最后匹配行

当我不增加位置控制参数时,会直接替换匹配行!

此处我们不指定firstmatch=yes参数,也不指定insertbeforeinsertafter参数,看看默认的效果。本节不测试配置文件校验。

编写剧本文件lineinfile.yml

- hosts: node1
tasks:
- name: Add download URL to nginx conf
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
regexp: 'Documentation'
line: "# * Download Nginx: http://nginx.org/en/download.html"
become: yes

检查剧本文件语法并执行:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Add download URL to nginx conf] **********************************************************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line replaced"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

此时,在节点查看配置文件:

[root@node1 ~]# head /etc/nginx/nginx.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Download Nginx: http://nginx.org/en/download.html

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.

可以看到,我们不增加位置控制时,当正则匹配到行后,原来第3行的# * Official Russian Documentation: http://nginx.org/ru/docs/被替换成了# * Download Nginx: http://nginx.org/en/download.html

4.4.2 修改首次匹配行

我不想对最后一个匹配行进行替换,只想对第一个匹配进行修改,则可以增加参数firstmatch=yes

我们将节点上的配置文件还原。

[root@node1 ~]# head -n 4 /etc/nginx/nginx.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/

[root@node1 ~]#

然后修改剧本文件:

- hosts: node1
tasks:
- name: Add download URL to nginx conf
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
regexp: 'Documentation'
line: "# * Download Nginx: http://nginx.org/en/download.html"
firstmatch: yes
become: yes

然后,再次执行剧本:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Add download URL to nginx conf] **********************************************************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line replaced"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

在节点上面查看配置文件前几行:

[root@node1 ~]# head -n 4 /etc/nginx/nginx.conf
# For more information on configuration, see:
# * Download Nginx: http://nginx.org/en/download.html
# * Official Russian Documentation: http://nginx.org/ru/docs/

[root@node1 ~]#

此时,可以看到,第2行的# * Official English Documentation: http://nginx.org/en/docs/被替换成了# * Download Nginx: http://nginx.org/en/download.html

说明配置firstmatch: yes参数生效了,改变了Ansible的行为了。

4.4.3 在指定行后插入数据

再次将节点上配置文件还原后,再进行测试。

修改后的剧本文件:

- hosts: node1
tasks:
- name: Add download URL to nginx conf
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
insertafter: 'Documentation'
line: "# * Download Nginx: http://nginx.org/en/download.html"
become: yes

执行剧本:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Add download URL to nginx conf] **********************************************************************************
changed: [node1] => {"backup": "", "changed": true, "msg": "line added"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

此时查看节点上配置文件前5行:

[root@node1 ~]# head -n 5 /etc/nginx/nginx.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
# * Download Nginx: http://nginx.org/en/download.html

[root@node1 ~]#

可以看到,第4行已经新增了一行,有了Nginx的下载链接信息了,在最后一次匹配(指定)行后增加了一行。

4.4.4 在指定行前插入数据

此处我们保留上一节的修改,不手动还原。再尝试在指定行的前一行插入数据。

修改后的剧本文件:

- hosts: node1
tasks:
- name: Add download URL to nginx conf
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
insertbefore: '^# \* Official Russian Documentation: http://nginx.org/ru/docs/'
line: "# * Download Nginx: http://nginx.org/en/download.html"
backup: yes
state: present
become: yes

然后,再次执行剧本,执行后的文件前几行如下:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Add download URL to nginx conf] **********************************************************************************
changed: [node1] => {"backup": "/etc/nginx/nginx.conf.10326.2022-10-29@20:42:55~", "changed": true, "msg": "line added"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

此时在节点1上面查看nginx.conf的前几行:

[root@node1 ~]# head -n 6 /etc/nginx/nginx.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Download Nginx: http://nginx.org/en/download.html
# * Official Russian Documentation: http://nginx.org/ru/docs/
# * Download Nginx: http://nginx.org/en/download.html

[root@node1 ~]#

可以看到,的确是在原来的第3行的# * Official Russian Documentation: http://nginx.org/ru/docs/前面增加了一行# * Download Nginx: http://nginx.org/en/download.html

特别要注意剧本中insertbefore: '^# \* Official Russian Documentation: http://nginx.org/ru/docs/'处的\*,表示星号,如果不带反斜杠,则会匹配多个空格,此时会匹配不上。

如我们去掉这个斜杠,再执行剧本,可以看到没有变化。

[ansible@master ansible_playbooks]$ cat lineinfile.yml
- hosts: node1
tasks:
- name: Add download URL to nginx conf
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
insertbefore: '^# * Official Russian Documentation: http://nginx.org/ru/docs/'
line: "# * Download Nginx: http://nginx.org/en/download.html"
backup: yes
state: present
become: yes
[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Add download URL to nginx conf] **********************************************************************************
ok: [node1] => {"backup": "", "changed": false, "msg": ""}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

可以在节点上使用grep命令测试一下:

# *星号前不带反斜杠\,没有匹配上
[root@node1 ~]# grep '^# * Official Russian Documentation: http://nginx.org/ru/docs/' /etc/nginx/nginx.conf

# *星号前带反斜杠\,匹配上一行
[root@node1 ~]# grep '^# \* Official Russian Documentation: http://nginx.org/ru/docs/' /etc/nginx/nginx.conf
# * Official Russian Documentation: http://nginx.org/ru/docs/
[root@node1 ~]#

可以看到,不带反斜杠时没有匹配上。

4.4.5 在文件头部插入数据

我们尝试在文件最前面增加一行内容。

编写剧本文件:

- hosts: node1
tasks:
- name: Insert the line at the beginning of the file
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
insertbefore: BOF
line: "# The first line"
backup: yes
state: present
become: yes

执行剧本:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Insert the line at the beginning of the file] ********************************************************************
changed: [node1] => {"backup": "/etc/nginx/nginx.conf.17579.2022-10-29@21:22:22~", "changed": true, "msg": "line added"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

在节点上面查看文件开头几行:

[root@node1 ~]# head -n 5 /etc/nginx/nginx.conf
# The first line
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Download Nginx: http://nginx.org/en/download.html
# * Official Russian Documentation: http://nginx.org/ru/docs/
[root@node1 ~]#

可以看到已经在第一行添加了# The first line说明修改生效了。

4.4.6 反向引用

我们使用反向引用替换文件中的行,如想将第3行中的# * Official English Documentation: http://nginx.org/en/docs/替换成中文的文档信息# * Official Chinese Documentation: http://nginx.org/cn/docs/

我们可以这么做。 编写剧本文件:

- hosts: node1
tasks:
- name: Add new language documentation
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
regexp: '(.*)English(.*)en(.*)'
line: '\1Chinese\2cn\3'
backrefs: yes
backup: yes
state: present
become: yes

执行剧本:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Add new language documentation] **********************************************************************************
changed: [node1] => {"backup": "/etc/nginx/nginx.conf.23537.2022-10-29@22:02:57~", "changed": true, "msg": "line replaced"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

可以看到打印出消息line replaced行已经被替换。

而执行剧本前和执行剧本后,在节点上查看文件前5行的内容对比:

[root@node1 ~]# head -n 5 /etc/nginx/nginx.conf
# The first line
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Download Nginx: http://nginx.org/en/download.html
# * Official Russian Documentation: http://nginx.org/ru/docs/
[root@node1 ~]# head -n 5 /etc/nginx/nginx.conf
# The first line
# For more information on configuration, see:
# * Official Chinese Documentation: http://nginx.org/cn/docs/
# * Download Nginx: http://nginx.org/en/download.html
# * Official Russian Documentation: http://nginx.org/ru/docs/

看看修改后的文件与备份文件的差异:

[root@node1 ~]# diff /etc/nginx/nginx.conf /etc/nginx/nginx.conf.23537.2022-10-29@22:02:57~
3c3
< # * Official Chinese Documentation: http://nginx.org/cn/docs/
---
> # * Official English Documentation: http://nginx.org/en/docs/
[root@node1 ~]#

可以看到,第3行除了English被替换成Chineseen替换成了cn,其他部分都直接使用了原来的内容,剧本中参数line: '\1Chinese\2cn\3'的正则表达式'\1Chinese\2cn\3'\1表示匹配到原来行中的第一部分# * Official ,而\2表示匹配到原来行中的第二部分 Documentation: http://nginx.org/,而\3表示匹配到原来行中的第三部分/docs/

4.4.7 添加多行

编写剧本:

- hosts: node1
tasks:
- name: Add multiple lines
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
line: "{{ item }}"
backup: yes
state: present
with_items:
- '# Orange'
- '# Apple'
- '# Banana'
become: yes

如果还有别的行需要添加,只需要在with_items下面增加新的行即可。

检查并执行剧本:

[ansible@master ansible_playbooks]$ ansible-lint lineinfile.yml
[ansible@master ansible_playbooks]$ ansible-playbook lineinfile.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [node1] ***********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [node1]

TASK [Add multiple lines] **********************************************************************************************
changed: [node1] => (item=# Orange) => {"ansible_loop_var": "item", "backup": "/etc/nginx/nginx.conf.29943.2022-10-29@22:48:28~", "changed": true, "item": "# Orange", "msg": "line added"}
changed: [node1] => (item=# Apple) => {"ansible_loop_var": "item", "backup": "/etc/nginx/nginx.conf.30031.2022-10-29@22:48:28~", "changed": true, "item": "# Apple", "msg": "line added"}
changed: [node1] => (item=# Banana) => {"ansible_loop_var": "item", "backup": "/etc/nginx/nginx.conf.30119.2022-10-29@22:48:28~", "changed": true, "item": "# Banana", "msg": "line added"}

PLAY RECAP *************************************************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[ansible@master ansible_playbooks]$

在节点查看配置文件最后几行:

[root@node1 ~]# tail /etc/nginx/nginx.conf
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }

}

# Orange
# Apple
# Banana
[root@node1 ~]#

可以看到,多行内容添加成功。