複数ファイルに一括でinsert

いくつかのファイルにcopyrightを追加する必要があり調べたのでメモ

参考ページ
Unix Sed Tutorial: Append, Insert, Replace, and Count File Lines

sedを使えば簡単にできる
まずは簡単な場合。example.cの先頭に以下の一行を入れたいとき

// Copyright Hoge - hogehogehoge

以下のコマンドでOK

$ sed -i.tmp -e '1 i\
quote> // Copyright Hoge - hogehogehoge' example.c

コマンドライン上ではシングルクオート中に改行を入れるとquote>のようになる(使っているシェルに依るだろうが)

1は一行目の意味でi\で挿入の意味。行指定の所は正規表現も使えるので例えば/WORDなどとすれば各WORDという文字列のある行の前に挿入することができる。今回はファイルの頭に入れたいので1

-i.tmpは2つの意味があるが、-iとすると対象のファイルを上書きするようになる
-iに続けて入力した文字はバックアップファイルの拡張子となる
つまり、↑のコマンドを実行するとexample.cにはコピーライトが挿入され、挿入前のexample.cがexample.c.tmpとして保存される
注意点は-iの直後にスペースを空けてはいけないこと

カレントディレクトリ以下のすべての.cファイルに挿入したい場合はパイプでつなげる

$ find . -name '*.c' | xargs -n1 sed -i.tmp -e '1 i\
quote> // Copyright Hoge - hogehogehoge'

xargsの-n1は渡された値を1つずつ次のコマンドに渡すというオプション
デフォルトだと出来る限り多くの引数を並べた状態で次に渡るため今回の場合うまくいかない

挿入したものが複数行になってくるとコマンドラインでやるのは面倒になる
適当なファイルに挿入したい文字列を入れてそれを使いたい
挿入したい文字列のファイルをinsert.txtとすると以下のようにできる

# insert.txt
/*\
 * Copyright Hoge - hogehogehoge\
 * Copyright (C) 2014 Fuga Fuga\
 */

コマンド

$ find . -name '*.c' | xargs -n1 sed -i.tmp -e "1 i\\
quote> $(cat /path/to/insert.txt)"

ポイントは2つ
1. insert.txtの各行の最後尾にバックスラッシュを入れていること(ただし、最終行を除く!)
2. sedコマンド内で使うクオートをダブルクオートにすること
3. sedコマンド内のinsertを示す文字直後のバックスラッシュを2重にする

1.については$(cat /path/to/insert.txt)の部分がファイル内の文字列にそのまま置き換わるため、改行はバックスラッシュでエスケープしておく必要がある。よって最終行には要らない。というか最終行に入れるとエラーになる。

2.についてはシングルクオートだと$(cat /path/to/insert.txt)が評価されないため
3.については2.に関係するが、ダブルクオートにしたためシェルによって一度評価されてしまうため2重にする必要がある

sedに関してはこちらの本を読めば一通り網羅されている
けっこうボリュームあるが一度目を通しておくと、必要になったときにググれば使えるくらいにはなるため便利

sed & awkプログラミング 改訂版 (A nutshell handbook)

sed & awkプログラミング 改訂版 (A nutshell handbook)