修改文件单行内容
ansible.builtin.lineinfile
可以按行修改文件内容,一次修改一行,支持正则表达式。
选项名 |
类型 |
默认值 |
描述 |
attributes |
str |
null | 设置目标文件的 Linux 文件系统属性(attribute bits),作用类似于 |
backrefs |
bool |
false |
使用 |
backup |
bool |
false |
修改文件前是否创建备份。备份文件将带有时间戳后缀。 |
create |
bool |
false |
文件不存在时是否创建新文件。 |
firstmatch |
bool |
false |
如果为 |
group |
str |
null | 设置文件的属组。 |
insertafter |
str |
EOF |
在匹配到的行之后插入。如果为 |
insertbefore |
str |
null | 在匹配到的行之前插入。如果为 |
line |
str |
null | 要插入或替换的行内容。 |
mode |
raw |
null | 设置文件权限(八进制形式,或 |
others |
— |
— |
ansible.builtin.file 模块接受的所有参数在这里也同样有效。 |
owner |
str |
null | 设置文件的属主。 |
path |
path |
— |
必需项 ,目标文件的完整路径。 |
regexp |
str |
null | 匹配要替换行的正则表达式。 |
search_string |
str |
null | 替代 |
selevel |
str |
null | SELinux context 中的 level。 |
serole |
str |
null | SELinux context 中的 role。 |
setype |
str |
null | SELinux context 中的 type。 |
seuser |
str |
null | SELinux context 中的 user。 |
state |
str |
present |
设置为 |
unsafe_writes |
bool |
false |
是否禁用临时文件写入机制(兼容某些挂载类型如 NFS)。 |
validate |
str |
null | 在写入前校验文件内容的命令(如 |
常用选项:
选项名 |
类型 |
默认值 |
描述 |
backrefs |
bool |
false |
使用 |
backup |
bool |
false |
修改文件前是否创建备份。备份文件将带有时间戳后缀。 |
create |
bool |
false |
文件不存在时是否创建新文件。 |
owner |
str |
null | 设置文件的属主。 |
group |
str |
null | 设置文件的属组。 |
insertafter |
str |
EOF |
在匹配到的行之后插入。如果为 |
insertbefore |
str |
null | 在匹配到的行之前插入。如果为 |
line |
str |
null | 要插入或替换的行内容。 |
mode |
raw |
null | 设置文件权限(八进制形式,或 |
others |
— |
— |
ansible.builtin.file 模块接受的所有参数在这里也同样有效。 |
path |
path |
— |
必需项 ,目标文件的完整路径。 |
regexp |
str |
null | 匹配要替换行的正则表达式。 |
search_string |
str |
null | 替代 |
state |
str |
present |
设置为 |
validate |
str |
null | 在写入前校验文件内容的命令(如 |
- 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: Ensure the default Apache port is 8080
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
insertafter: '^#Listen '
line: Listen 8080
- 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
- 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
- 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
其他的选项都好理解,一眼就能看出来,
insertafter
和insertbefore
我解释一下,上边有个例子用了insertafter
,可以看到同时也使用了regexp
,加了这个和不加是有区别的,可以看下边的例子。
有个文件,内容如下:
[root@awx-1 ansible]# cat /tmp/test
#test
#test
Listen=80
#test
#test
#test
playbook
内容如下:
---
- name: test
hosts: localhost
tasks:
- name: Lineinfile test
ansible.builtin.lineinfile:
line: "Listen=8080"
#regexp: "^Listen="
insertafter: "^#Listen="
path: /tmp/test
playbook
想实现的是在 #Listen=
后添加 Listen=8080
,但是有个特殊情况,文件可能不包含 #Listen=
。regexp
被我注释了,我们看下没有 regexp
的结果:
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
Listen=80
#test
#test
#test
Listen=8080
可以看到,结果是在最后一行添加了 Listen=8080
,恢复 /tmp/test
,在演示下加了 regexp
的结果:
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
Listen=8080
#test
#test
#test
可以看到这回是将已有的 Listen=80
修改为 Listen=8080
。
也就是说
regexp
和insertafter
或insertbefore
组合使用时,能够保证文件最后只有一个Listen
存在。
最后做两个测试:
第一个
[root@awx-1 ansible]# cat /tmp/test
#test
#test
Listen=80
#Listen=80
#test
#test
#test
[root@awx-1 ansible]# cat test.yml
---
- name: test
hosts: localhost
tasks:
- name: Lineinfile test
ansible.builtin.lineinfile:
line: "Listen=8080"
#regexp: "^Listen="
insertafter: "^#Listen="
path: /tmp/test
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
Listen=80
#Listen=80
Listen=8080
#test
#test
#test
第二个:
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#test
#test
#test
[root@awx-1 ansible]# cat test.yml
---
- name: test
hosts: localhost
tasks:
- name: Lineinfile test
ansible.builtin.lineinfile:
line: "Listen=8080"
regexp: "^Listen="
insertbefore: "^#Listen="
path: /tmp/test
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#test
#test
#test
Listen=8080
第二个例子里,我特意使用了
insertbefore
可以看到在没有任何匹配时,只会在文件的最后进行追加。
最后总结下,regexp
跟 insertafter
或 insertbefore
进行匹配时,情况如下:
regexp
优先匹配,会修改regexp
匹配的行regexp
没有匹配到时,按照insertafter
或insertbefore
的逻辑进行匹配和修改都没有匹配到时,在文件的最后一行追加
修改文件多行内容
ansible.builtin.blockinfile
用于修改文件的多行内容。
说是修改,多数时候还是添加新的多行内容,这里强调一点,
ansible.builtin.blockinfile
通过标记来查找文件中已添加的多行内容。
参数名 |
类型 |
默认值 |
描述 |
append_newline |
bool |
false |
如果插入 |
attributes |
str |
null | 用于设置文件的高级属性(extended attributes),如 |
backup |
bool |
false |
在修改文件前创建备份。 |
block |
str |
null | 要插入的文本块内容(多行字符串) |
create |
bool |
false |
如果文件不存在则创建 |
group |
str |
null | 设置文件的所属组 |
insertafter |
str |
EOF |
插入内容到匹配行之后,或 |
insertbefore |
str |
null | 插入内容到匹配行之前,或 |
marker |
str |
# {mark} ANSIBLE MANAGED BLOCK |
控制 |
marker_begin |
str |
BEGIN |
自定义起始标记,用于替代 |
marker_end |
str |
END |
自定义结束标记,用于替代 |
mode |
str |
null | 设置文件权限(如 |
owner |
str |
null | 设置文件所有者 |
path |
str |
null | 目标文件路径 |
prepend_newline |
bool |
false |
是否在 |
selevel |
str |
null | SELinux 安全级别 |
serole |
str |
null | SELinux 角色 |
setype |
str |
null | SELinux 类型 |
seuser |
str |
null | SELinux 用户 |
state |
str |
present |
是否确保 block 存在或被删除( |
unsafe_writes |
bool |
false |
绕过临时文件机制直接写入文件(有风险) |
validate |
str |
null | 应用更改前对文件进行语法验证(如 |
常用选项:
参数名 |
类型 |
默认值 |
描述 |
append_newline |
bool |
false |
如果插入 |
backup |
bool |
false |
在修改文件前创建备份。 |
block |
str |
null | 要插入的文本块内容(多行字符串) |
create |
bool |
false |
如果文件不存在则创建 |
group |
str |
null | 设置文件的所属组 |
insertafter |
str |
EOF |
插入内容到匹配行之后,或 |
insertbefore |
str |
null | 插入内容到匹配行之前,或 |
marker |
str |
# {mark} ANSIBLE MANAGED BLOCK |
控制 |
marker_begin |
str |
BEGIN |
自定义起始标记,用于替代 |
marker_end |
str |
END |
自定义结束标记,用于替代 |
mode |
str |
null | 设置文件权限(如 |
owner |
str |
null | 设置文件所有者 |
path |
str |
null | 目标文件路径 |
prepend_newline |
bool |
false |
是否在 |
state |
str |
present |
是否确保 block 存在或被删除( |
validate |
str |
null | 应用更改前对文件进行语法验证(如 |
- name: Insert/Update "Match User" configuration block in /etc/ssh/sshd_config prepending and appending a new line
ansible.builtin.blockinfile:
path: /etc/ssh/sshd_config
append_newline: true
prepend_newline: true
block: |
Match User ansible-agent
PasswordAuthentication no
- name: Insert/Update eth0 configuration stanza in /etc/network/interfaces
(it might be better to copy files into /etc/network/interfaces.d/)
ansible.builtin.blockinfile:
path: /etc/network/interfaces
block: |
iface eth0 inet static
address 192.0.2.23
netmask 255.255.255.0
- name: Insert/Update configuration using a local file and validate it
ansible.builtin.blockinfile:
block: "{{ lookup('ansible.builtin.file', './local/sshd_config') }}"
path: /etc/ssh/sshd_config
backup: yes
validate: /usr/sbin/sshd -T -f %s
- name: Insert/Update HTML surrounded by custom markers after <body> line
ansible.builtin.blockinfile:
path: /var/www/html/index.html
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK -->"
insertafter: "<body>"
block: |
<h1>Welcome to {{ ansible_hostname }}</h1>
<p>Last updated on {{ ansible_date_time.iso8601 }}</p>
- name: Remove HTML as well as surrounding markers
ansible.builtin.blockinfile:
path: /var/www/html/index.html
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK -->"
block: ""
- name: Add mappings to /etc/hosts
ansible.builtin.blockinfile:
path: /etc/hosts
block: |
{{ item.ip }} {{ item.name }}
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item.name }}"
loop:
- { name: host1, ip: 10.10.1.10 }
- { name: host2, ip: 10.10.1.11 }
- { name: host3, ip: 10.10.1.12 }
这里举个例子来验证 ansible.builtin.blockinfile
是如何确认文本块的:
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#block test
#test
#test
[root@awx-1 ansible]# cat test.yml
---
- name: test
hosts: localhost
tasks:
- name: Lineinfile test
ansible.builtin.blockinfile:
path: /tmp/test
append_newline: true
prepend_newline: true
block: |
address 192.168.1.1
netmask 255.255.255.0
gateway 192.168.1.254
insertafter: "^#block"
marker: "# {mark} block test"
marker_begin: "one"
marker_end: "two"
#state: absent
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#block test
# one block test
address 192.168.1.1
netmask 255.255.255.0
gateway 192.168.1.254
# two block test
#test
#test
[root@awx-1 ansible]# cat test.yml
---
- name: test
hosts: localhost
tasks:
- name: Lineinfile test
ansible.builtin.blockinfile:
path: /tmp/test
append_newline: true
prepend_newline: true
block: |
address 192.168.1.1
netmask 255.255.255.0
gateway 192.168.1.254
insertafter: "^#block"
marker: "# {mark} block test"
#marker_begin: "one"
#marker_end: "two"
#state: absent
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#block test
# BEGIN block test
address 192.168.1.1
netmask 255.255.255.0
gateway 192.168.1.254
# END block test
# one block test
address 192.168.1.1
netmask 255.255.255.0
gateway 192.168.1.254
# two block test
#test
#test
可以看到添加 block
的时候会在 block
的开头和结尾添加一个标记(标记一般是 #
开头,表示注释),通过 marker_begin
和 marker_end
修改标记的之后,会重新添加 block
,由此可见 block
的管理依赖标记,所以在添加新的 block
的时候记得保证标记的唯一性。
还有个小问题,append_newline
可以在 block
的末尾添加一个换行符来和旧内容分隔,但是每次删除重新添加时都会多一个换行符:
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#block test
#test
#test
[root@awx-1 ansible]# cat test.yml
---
- name: test
hosts: localhost
tasks:
- name: Lineinfile test
ansible.builtin.blockinfile:
path: /tmp/test
append_newline: true
prepend_newline: true
block: |
address 192.168.1.1
netmask 255.255.255.0
insertafter: "^#block"
#state: absent
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#block test
# BEGIN ANSIBLE MANAGED BLOCK
address 192.168.1.1
netmask 255.255.255.0
# END ANSIBLE MANAGED BLOCK
#test
#test
[root@awx-1 ansible]# cat test.yml
---
- name: test
hosts: localhost
tasks:
- name: Lineinfile test
ansible.builtin.blockinfile:
path: /tmp/test
append_newline: true
prepend_newline: true
block: |
address 192.168.1.1
netmask 255.255.255.0
insertafter: "^#block"
state: absent
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#block test
#test
#test
[root@awx-1 ansible]# ansible-playbook test.yml
...output omitted...
[root@awx-1 ansible]# cat /tmp/test
#test
#test
#block test
# BEGIN ANSIBLE MANAGED BLOCK
address 192.168.1.1
netmask 255.255.255.0
# END ANSIBLE MANAGED BLOCK
#test
#test