もっと色々あると思いますが、基本的にはlineinfile
モジュール、replace
モジュールシェルあたりを使って、どうしようもないとシェルというのが一般的でしょうか。
方法 | メリット | デメリット |
---|---|---|
lineinfile モジュール |
ある程度複雑な行の指定が可能 | 該当行が複数あると難あり |
replace モジュール |
複数箇所の変更が可能 | 複雑な行指定ができない |
シェル |
sed やawk で何でもできる |
面倒、冪等性の確保はユーザ次第 |
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
: 書き換えたい内容
シェル
基本的には、lininfile
とreplace
で事足りるのですが、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