Ansible模块——文件内容修改
修改文件单行内容
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 modeansible.builtin.lineinfile:path: /etc/selinux/configregexp: '^SELINUX='line: SELINUX=enforcing- name: Make sure group wheel is not in the sudoers configurationansible.builtin.lineinfile:path: /etc/sudoersstate: absentregexp: '^%wheel'- name: Replace a localhost entry with our ownansible.builtin.lineinfile:path: /etc/hostsregexp: '^127\.0\.0\.1'line: 127.0.0.1 localhostowner: rootgroup: rootmode: '0644'- name: Ensure the default Apache port is 8080ansible.builtin.lineinfile:path: /etc/httpd/conf/httpd.confregexp: '^Listen 'insertafter: '^#Listen 'line: Listen 8080- name: Add a line to a file if the file does not exist, without passing regexpansible.builtin.lineinfile:path: /tmp/testfileline: 192.168.1.99 foo.lab.net foocreate: yes- name: Ensure the JBoss memory settings are exactly as neededansible.builtin.lineinfile:path: /opt/jboss-as/bin/standalone.confregexp: '^(.*)Xms(\d+)m(.*)$'line: '\1Xms${xms}m\3'backrefs: yes- name: Validate the sudoers file before savingansible.builtin.lineinfile:path: /etc/sudoersstate: presentregexp: '^%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: testhosts: localhosttasks:- name: Lineinfile testansible.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: testhosts: localhosttasks:- name: Lineinfile testansible.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: testhosts: localhosttasks:- name: Lineinfile testansible.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 lineansible.builtin.blockinfile:path: /etc/ssh/sshd_configappend_newline: trueprepend_newline: trueblock: |Match User ansible-agentPasswordAuthentication 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/interfacesblock: |iface eth0 inet staticaddress 192.0.2.23netmask 255.255.255.0- name: Insert/Update configuration using a local file and validate itansible.builtin.blockinfile:block: "{{ lookup('ansible.builtin.file', './local/sshd_config') }}"path: /etc/ssh/sshd_configbackup: yesvalidate: /usr/sbin/sshd -T -f %s- name: Insert/Update HTML surrounded by custom markers after <body> lineansible.builtin.blockinfile:path: /var/www/html/index.htmlmarker: "<!-- {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 markersansible.builtin.blockinfile:path: /var/www/html/index.htmlmarker: "<!-- {mark} ANSIBLE MANAGED BLOCK -->"block: ""- name: Add mappings to /etc/hostsansible.builtin.blockinfile:path: /etc/hostsblock: |{{ 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: testhosts: localhosttasks:- name: Lineinfile testansible.builtin.blockinfile:path: /tmp/testappend_newline: trueprepend_newline: trueblock: |address 192.168.1.1netmask 255.255.255.0gateway 192.168.1.254insertafter: "^#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.1netmask 255.255.255.0
gateway 192.168.1.254
# two block test#test
#test[root@awx-1 ansible]# cat test.yml
---
- name: testhosts: localhosttasks:- name: Lineinfile testansible.builtin.blockinfile:path: /tmp/testappend_newline: trueprepend_newline: trueblock: |address 192.168.1.1netmask 255.255.255.0gateway 192.168.1.254insertafter: "^#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.1netmask 255.255.255.0
gateway 192.168.1.254
# END block test# one block test
address 192.168.1.1netmask 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: testhosts: localhosttasks:- name: Lineinfile testansible.builtin.blockinfile:path: /tmp/testappend_newline: trueprepend_newline: trueblock: |address 192.168.1.1netmask 255.255.255.0insertafter: "^#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.1netmask 255.255.255.0
# END ANSIBLE MANAGED BLOCK#test
#test
[root@awx-1 ansible]# cat test.yml
---
- name: testhosts: localhosttasks:- name: Lineinfile testansible.builtin.blockinfile:path: /tmp/testappend_newline: trueprepend_newline: trueblock: |address 192.168.1.1netmask 255.255.255.0insertafter: "^#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.1netmask 255.255.255.0
# END ANSIBLE MANAGED BLOCK#test
#test