作曲・浄書・指導・音響

金沢音楽制作

金沢音楽制作では、楽曲・楽譜の制作と、作曲や写譜などレッスンを行っています。

複数のファイルの内容を一括で置換する

ディレクトリ配下にある、大量のファイルの内容を一括で置換する方法について述べる。grepとsedの2つのコマンドを使用するが、GNUコマンドを前提としている。したがって、macOSといったBSD環境の場合は、別の工夫が必要となるだろう。環境:grep (GNU) 3.4、sed (GNU) 4.8

本稿では、htmlに記述されている外部CSSのリンクを変更する例をあげる。外部cssのmain_0320.cssを、main_0401.cssに変更したい。また、補遺として、カレントディレクトリに限る場合も述べておきたい。

コマンドの一例

次のコマンドで、ディレクトリ配下のファイルの内容を一括で置換できる。

$ grep -rol 'main.*css' . | xargs sed -i -e 's/0320/0401/'

grepで処理の対象をマッチさせ、xargsでsedの引数にgrepの結果を渡す。sedの引数には、[filename]を記述するので、grepで条件にマッチした内容ではなく、ファイル名を記述する必要がある。grepにオプション「-l」をつけると、マッチしたファイル名が取得できる。sedにつけられたオプションの「-e」は、直後にスクリプト(この場合は、's/0320/0401/'という置換スクリプト)がくることを示す(省略可)。したがって、「sed -e -i 's...」という記述はエラーになる

このコマンドが一体何をしているのか、詳細を見ていこう。

[目次へ]

ディレトクリの構造

今回の例で使用するディレクトリ構造は以下の通りである。この内のhtmlファイルの内容を書き換えたい。

$ ls
blog css index.html
.
|--- blog
|    |--- 2020
|    |    |--- n101.html
|    |    |--- n102.html
|    |    `--- n103.html
|    `--- index.html
|--- css 
|    |--- main_0320.css
|    `--- sub.css
`--- index.html

[目次へ]

grepで処理の対象を表示

まず、grepでcssのリンクを探す。オプションの「-r」は、再帰的な検索を可能にする。オプション「-o」は、マッチした文字列のみを表示するもので、見やすくするだけではなく、意図しない置換を防ぐ、絞り検索のような役割もある。ファイル名は、正規表現を使うのでシングルクォート('')で囲む。もし、シングルクォートをつけないと、bashが評価してしまう。最後に任意の検索の開始ディレクトリを指定する。今回は、カレントディレクトリ配下を検索したいので、「.」をつける。なお、「-r」を用いない場合は、ファイル名、あるいは「*」などを指定する。

すると、以下のようにマッチした内容が表示される。正規表現は状況に合わせて適宜変更されたい。

$ grep -ro 'main.*css' .
./blog/2020/n101.html:main_0320.css
./blog/2020/n102.html:main_0320.css
./blog/2020/n103.html:main_0320.css
./blog/index.html:main_0320.css
./index.html:main_0320.css

[目次へ]

sedの引数にgrepの結果を渡す

つぎに、sedの引数にgrepの結果を渡す。置換の内容を確認すると、cssの名前が変わっていることが確認できる。この操作は表示させただけであり、実際にファイルの中身が書き換えられたわけではない。

$ grep -ro 'main.*css' . | sed 's/0320/0401.css/'
./blog/2020/n101.html:main_0401.css
./blog/2020/n102.html:main_0401.css
./blog/2020/n103.html:main_0401.css
./blog/index.html:main_0401.css
./index.html:main_0401.css

さて、sedの引数にgrepの結果を渡すには、xargsを使う。sedの引数には、[filename]を記述するので、grepで条件にマッチしたファイル名を取得しなければならない。grepにオプション「-l」をつけて、マッチしたファイル名を取得する。sedにつけられたオプションの「-e」は、直後にスクリプト(この場合は、's/0320/0401/')がくることを示す(省略可)。したがって、「sed -e -i 's...」という記述はエラーになる。

$ grep -rol 'main.*css' . | xargs sed -i -e 's/0320/0401/'

なお、sedでファイルの中身を書き換えるには、オプション「-i」をつける。この-iはGNU sedのオプションで、BSD sedでは挙動が違うので注意が必要である。筆者もmacOSを使うことがあるが、基本的なコマンドは、GNUに置き換えて使っている。

$ sed -i -e 's/0320/0401.css/' [filename]

[目次へ]

カレントファイルの処理

カレントファイルのみを置換したい場合は、sedだけで可能である。たとえば、日付の区切り文字を「2020/03/05」から「2020-03-05」にしたいとする。sedの区切り文字は「/」以外にも、「!」「#」などが使える。/を置換する場合は、「\/」と\でエスケープが必要なので、/以外の区切り文字を使った方が可読性が上がる場合がある。

$ grep -o '[0-9]\{4\}/[0-9]\{2\}/[0-9]\{2\}' *
n101.html:2020/01/04
n102.html:2020/01/08
n103.html:2020/01/22
$ sed -i -e 's#\([0-9]\{4\}\)/\([0-9]\/{2\}\)/\([0-9]\{2\}\)#\1-\2-\3#' *
$ grep -o '[0-9]\{4\}-[0-9]\{2\}-[0-9]-{2\}' *
n101.html:2020-01-04
n102.html:2020-01-08
n103.html:2020-01-22

しかし、grepを通してからsedした方が安全かつ簡単である。sedで扱う正規表現が簡単になるだろう。たとえば、次のような正規表現も可能かもしれない。

$ grep -o '[0-9].../../..' *
n101.html:2020/01/04
n102.html:2020/01/08
n103.html:2020/01/22
$ grep -o '[0-9].../../..' * | xargs sed -i -e 's!/!-!g'
$ grep -o '[0-9]...-..-..' * 
n101.html:2020-01-04
n102.html:2020-01-08
n103.html:2020-01-22

[目次へ]

[ノート一覧へ]

  • 公開日:2020-03-23