Ansible模块——文件内容修改

修改文件单行内容

ansible.builtin.lineinfile 可以按行修改文件内容,一次修改一行,支持正则表达式。

选项名 类型 默认值 描述
attributes str null 设置目标文件的 Linux 文件系统属性(attribute bits),作用类似于 chattr 命令。例如:+i 设置为不可变,+a 设置为只能追加写入等。
backrefs bool false 使用 regexpline 时,是否启用后向引用替换。(使用正则分组时需要设置为 true)。
backup bool false 修改文件前是否创建备份。备份文件将带有时间戳后缀。
create bool false 文件不存在时是否创建新文件。
firstmatch bool false 如果为 true,当 regexp 匹配多行时,只对第一行执行操作。
group str null 设置文件的属组。
insertafter str EOF 在匹配到的行之后插入。如果为 EOF,则追加到末尾。
insertbefore str null 在匹配到的行之前插入。如果为 BOF,则追加到开头。
line str null 要插入或替换的行内容。
mode raw null 设置文件权限(八进制形式,或 preserve 保留现有)。
others --- --- ansible.builtin.file 模块接受的所有参数在这里也同样有效。
owner str null 设置文件的属主。
path path --- 必需项 ,目标文件的完整路径。
regexp str null 匹配要替换行的正则表达式。
search_string str null 替代 regexp 的简易字符串匹配(不支持正则)。
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 设置为 present 插入或修改行,或 absent 删除匹配行。
unsafe_writes bool false 是否禁用临时文件写入机制(兼容某些挂载类型如 NFS)。
validate str null 在写入前校验文件内容的命令(如 nginx -t -c %s)。

常用选项:

选项名 类型 默认值 描述
backrefs bool false 使用 regexpline 时,是否启用后向引用替换。(使用正则分组时需要设置为 true)。
backup bool false 修改文件前是否创建备份。备份文件将带有时间戳后缀。
create bool false 文件不存在时是否创建新文件。
owner str null 设置文件的属主。
group str null 设置文件的属组。
insertafter str EOF 在匹配到的行之后插入。如果为 EOF,则追加到末尾。
insertbefore str null 在匹配到的行之前插入。如果为 BOF,则追加到开头。
line str null 要插入或替换的行内容。
mode raw null 设置文件权限(八进制形式,或 preserve 保留现有)。
others --- --- ansible.builtin.file 模块接受的所有参数在这里也同样有效。
path path --- 必需项 ,目标文件的完整路径。
regexp str null 匹配要替换行的正则表达式。
search_string str null 替代 regexp 的简易字符串匹配(不支持正则)。
state str present 设置为 present 插入或修改行,或 absent 删除匹配行。
validate str null 在写入前校验文件内容的命令(如 nginx -t -c %s)。
复制代码
- 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

其他的选项都好理解,一眼就能看出来,insertafterinsertbefore 我解释一下,上边有个例子用了 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

也就是说 regexpinsertafterinsertbefore组合使用时,能够保证文件最后只有一个 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 可以看到在没有任何匹配时,只会在文件的最后进行追加。

最后总结下,regexpinsertafterinsertbefore 进行匹配时,情况如下:

  • regexp 优先匹配,会修改 regexp 匹配的行

  • regexp 没有匹配到时,按照 insertafterinsertbefore 的逻辑进行匹配和修改

  • 都没有匹配到时,在文件的最后一行追加

修改文件多行内容

ansible.builtin.blockinfile 用于修改文件的多行内容。

说是修改,多数时候还是添加新的多行内容,这里强调一点,ansible.builtin.blockinfile 通过标记来查找文件中已添加的多行内容。

参数名 类型 默认值 描述
append_newline bool false 如果插入 block 后,新末行没有换行符,则自动加一行换行,保持标准文本格式
attributes str null 用于设置文件的高级属性(extended attributes),如 i(不可变)、a(只能追加)。相当于执行 chattr,仅在支持该功能的文件系统(如 ext4)中有效
backup bool false 在修改文件前创建备份。
block str null 要插入的文本块内容(多行字符串)
create bool false 如果文件不存在则创建
group str null 设置文件的所属组
insertafter str EOF 插入内容到匹配行之后,或 EOF 表示文件结尾
insertbefore str null 插入内容到匹配行之前,或 BOF 表示文件开头
marker str # {mark} ANSIBLE MANAGED BLOCK 控制 block 标记的格式,{mark} 会被替换为 BEGINEND
marker_begin str BEGIN 自定义起始标记,用于替代 {mark}
marker_end str END 自定义结束标记,用于替代 {mark}
mode str null 设置文件权限(如 0644
owner str null 设置文件所有者
path str null 目标文件路径
prepend_newline bool false 是否在 block 前添加换行,防止与上一行粘连
selevel str null SELinux 安全级别
serole str null SELinux 角色
setype str null SELinux 类型
seuser str null SELinux 用户
state str present 是否确保 block 存在或被删除(present/absent
unsafe_writes bool false 绕过临时文件机制直接写入文件(有风险)
validate str null 应用更改前对文件进行语法验证(如 nginx -t -c %s

常用选项:

参数名 类型 默认值 描述
append_newline bool false 如果插入 block 后,新末行没有换行符,则自动加一行换行,保持标准文本格式
backup bool false 在修改文件前创建备份。
block str null 要插入的文本块内容(多行字符串)
create bool false 如果文件不存在则创建
group str null 设置文件的所属组
insertafter str EOF 插入内容到匹配行之后,或 EOF 表示文件结尾
insertbefore str null 插入内容到匹配行之前,或 BOF 表示文件开头
marker str # {mark} ANSIBLE MANAGED BLOCK 控制 block 标记的格式,{mark} 会被替换为 BEGINEND(注意标记里必须含有 {mark} 字段)
marker_begin str BEGIN 自定义起始标记,用于替代 {mark}
marker_end str END 自定义结束标记,用于替代 {mark}
mode str null 设置文件权限(如 0644
owner str null 设置文件所有者
path str null 目标文件路径
prepend_newline bool false 是否在 block 前添加换行,防止与上一行粘连
state str present 是否确保 block 存在或被删除(present/absent
validate str null 应用更改前对文件进行语法验证(如 nginx -t -c %s
复制代码
- 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_beginmarker_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
相关推荐
万山y1 小时前
es快速上手(从MySQL角度)
mysql·elasticsearch·jenkins
Listennnn1 小时前
Neo4j数据库
数据库·人工智能·neo4j
Liu1bo2 小时前
【MySQL】库与表的操作
数据库·mysql·oracle
冬瓜的编程笔记2 小时前
【MySQL成神之路】MySQL常用语法总结
数据库·mysql
YJQ99673 小时前
Redis配置与优化:提升NoSQL数据库性能的关键策略
数据库·redis·nosql
@Turbo@3 小时前
【QT】一个界面中嵌入其它界面(二)
开发语言·数据库·qt
小白的码BUG之路3 小时前
Elasticsearch-kibana索引操作
大数据·数据库·elasticsearch
八股文领域大手子4 小时前
MySQL死锁:面试通关“三部曲”心法
数据库·mysql·面试
dianzouyou1234564 小时前
钉钉报销与金蝶付款单系统对接技术揭秘
大数据·数据库·钉钉