Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
232
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Ansibleでファイルの行を書き換える3つの方法

もっと色々あると思いますが、基本的にはlineinfileモジュール、replaceモジュールシェルあたりを使って、どうしようもないとシェルというのが一般的でしょうか。

方法 メリット デメリット
lineinfile モジュール ある程度複雑な行の指定が可能 該当行が複数あると難あり
replace モジュール 複数箇所の変更が可能 複雑な行指定ができない
シェル sedawkで何でもできる 面倒、冪等性の確保はユーザ次第

lineinfile

こんな感じで使います。

- name: "設定の修正(1)"
  lineinfile: >-
    dest='/path/to/file/'
    state=present
    backrefs=yes
    regexp='^#?\s*ServerTokens'
    line='ServerTokens Prod'
  • dest: 書き換える対象のファイル
  • state=present: 「この行があるべき」ことを示す
  • backrefs=yes: 正規表現内のバックスラッシュが使えるように
  • regexp: 書き換えたい行にマッチする正規表現
  • line: 書き換えたい内容

他にもオプションがいろいろあるので、詳細は公式ドキュメントをどうぞ。

ひとつのファイル内で複数箇所を書き換える場合は、with_itemsと組み合わせます。

- name: "設定の修正(2)"
  lineinfile: >-
    dest='/path/to/file'
    state=present
    backrefs=yes
    regexp='{{ item.regexp }}'
    line='{{ item.line }}'
  with_items:
  - regexp: '^#?\s*ServerTokens'
    line: 'ServerTokens Prod'
  - regexp: '^#?\s*ServerSignature'
    line: 'ServerSignature Off'

replace

公式ドキュメントを見ると分かりますが、lineinfileと比べるとオプションが多くありません。変更は行単位でなくてもOK。複数箇所にマッチする場合も、すべてを書き換えます。Pythonのマルチラインモードで動作するので、^/$は各行の先頭/最後にマッチします。そのため、複数行に渡る正規表現はマッチしません。

- name: "設定の修正(3)"
  replace: >-
    dest='/path/to/file'
    regexp='AllowOverride None'
    replace='AllowOverride All'
  • dest: 書き換える対象のファイル
  • regexp: 書き換えたい部分にマッチする正規表現
  • replace: 書き換えたい内容

シェル

基本的には、lininfilereplaceで事足りるのですが、Apacheのhttpd.confのように巨大なものだとそうもいかなくなってきます。例えば、次の例のように^\s*AllowOverrideだけで特定しようとすると、どちらなのか判断ができません。

...
<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>

<Directory "/var/www/html">
    Options FollowSymLinks
    AllowOverride None
</Directory>
...

このうちの後者だけ変更したければ、例えば次のように書けます。ちょっと分かりにくいのですが、前半は変数に代入しているだけです。

- name: "設定の修正(4)"
  shell: >-
    c='/path/to/file' &&
    k='<Directory "\/var\/www\/html">' &&
    s='AllowOverride None' &&
    r='AllowOverride All' &&
    mv $c $c.backup &&
    awk "/$k/{f=1} f==1&&/$s/{sub(/.+/,\"$r\"); f=0} 1" $c.backup > $c

awkコマンド部分を抜き出すとこうなります。

awk "
  /$k/{f=1}
  f==1&&/$s/{sub(/.+/,\"$r\"); f=0}
  1
"
  • $kに該当する行が見つかったら
  • $sに該当する行を見つけ
  • $rに置き換える

の意です。※この部分、richmikanさんに教えてもらいました。感謝。

コマンドの選択肢

ここではawkで処理していますが、使えるコマンドとしては次の3つが有力候補。

  • awk: sedよりは、扱いやすい
  • sed: 久々に使うと、モダン(?)な正規表現との違いがちょとつらい
  • perl: モダンな正規表現になれた人にやさしい

なお、次の3つはだいたい同じ意味になります。

$ awk '/search/gsub(/search/replace)' target.txt > target.txt
$ sed -i -e 's/search/replace/g' target.txt
$ perl -p -i -e 's/search/replace/g' target.txt

複数行に対して、正規表現のみでマッチさせるには、perlが比較的簡単です。-p0777オプションと、マッチ演算子sがポイントです。

$ perl -p0777 -i -e 's/search/replace/gs' target.txt

サンプル

実際に動く(はず)のサンプルをひとつ置いておきます。CentOSで、Apacheを動かすための最小構成(?)です。

---
- hosts: "webservers"
  sudo: yes
  vars:
    document_root: "/var/www/html"
    httpd_conf: "/etc/httpd/conf/httpd.conf"
  tasks:
    - name: "yumのアップデート"
      command: "yum -y update"

    - name: "パッケージのインストール"
      yum: >-
        name={{ item }}
        state=present
      with_items:
        - "httpd"
        - "libmcrypt"
        - "libselinux-python"

    - name: "httpd.confの修正 (1)"
      lineinfile: >-
        dest='{{ httpd_conf }}'
        state=present
        backrefs=yes
        regexp='{{ item.regexp }}'
        line='{{ item.line }}'
      with_items:
        - regexp: '^#?\s*ServerTokens'
          line: 'ServerTokens Prod'
        - regexp: '^#?\s*ServerSignature'
          line: 'ServerSignature Off'

    - name: "httpd.confの修正 (2)"
      shell: >-
        c='{{ httpd_conf }}' &&
        k='<Directory "{{ document_root|replace("/","\/") }}">' &&
        t='AllowOverride None' &&
        s='/None$/' &&
        r='All' &&
        awk "/$k/{f=1} f==1&&/$t/{sub($s,\"$r\"); f=0} 1" $c > $c

    - name: "サービスを起動: httpd"
      service: >-
        name= httpd
        state=started
        enabled=yes

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
232
Help us understand the problem. What are the problem?