diffとsedコマンドの使い方
ちょっとタイトルが変になりましたが、いろいろありまして、ファイル比較をやらなきゃならない羽目になりました。
しかも、使う環境が、Debianになるのか、CentOSになるのか、RedHatになるのかも分からなく、どちらかと言えば、Linux系のマシンのどこからでも利用可能である必要があり、C言語のコンパイル系の実行系アプリでは、いろいろと制約が多い試練であります。
テキストベースでファイル同士の比較を行うコマンドdiffですが、ファイル同士の完全一致を比較し、その差分を出力してくれますが、
- ファイルの中のある文言は比較してほしくない、
- ファイルの中のある文言は値としてAという値からBという値までは差分があっても同じものとして認識して欲しい。
- 改行がちょっと違ってもちゃんと同じものとして認識してほしい。
- そもそも同じようなファイルじゃなくても、ファイルの一部とパタン比較をして欲しい。
いろいろといちゃもんつけたらdiffコマンドにとってはそれは無理ですといいたくもなります。だからと言って、今回はかゆいところに手の届く専用のアプリを作るわけにも行かなくて、既存のコマンドでやりくりして、シェルスクリプトだけでなんとかしなければならない縛りがついているのならと思ったら、、、
この二つのコマンド(diff and sed)をマスタせよ!
という使命に辿りつきました。
(あ!今、思うとPerlは使っても良かったかも!)
diffコマンド
基本的な動作は二つのファイルの完全一致比較を行うコマンドであります。
$ echo 1 > text1.txt
$ echo 2 > text2.txt
$ diff text1.txt text2.txt1c1
< 1
---
> 2
こんな感じの出力が得られます。
---を挟んで、上に元となるファイル部分と、下に比較するファイル部分の差分が表示されます。この「1c1」の'c'の意味はおそらくchangedまたはconflictのでしょう。
'c'以外にも、'd'(削除された)や'a'(追加された)というメッセージも出力してくれます。
例:2,6d1
元のファイルの2行目~6行目までが削除され、それは新しいファイルの1行目に該当するだろうの意味になります。
「diff -r ディレクトリ1 ディレクトリ2」のようにディレクトリ内のファイルを丸ごと比較も可能であるようですが、そんなことは必要な場面に出合ったことがなくやったことがありません。
その他に以下のようなオプションも用意されているのですが、なんだか使い勝手がイマイチな機能ばかりのように思えます。
比較方法を制御するオプション
- -b スペースの数が違うだけの場合は無視します。
- -i 大文字と小文字の区別を無視します。
- -q ファイルが違うかどうかだけを報告します。
- -r ディレクトリを比較するときサブディレクトリも再帰的に比較します。
- -s ファイルに違いがない場合も報告します。
比較結果を制御するオプション
- -c context出力形式で出力します。
- -e edスクリプト形式で出力します。
- -n RCS形式で出力します。
- -u unified出力形式で出力します。
ということで、diffはデフォルト状態で比較することにして、diffコマンドをかける対象をsedコマンドで制御して、1行のみの一時ファイルを生成して加工したデータ同士を比較することにしました。
sedコマンド
文字列を全置換したり、行単位で抽出したり、削除したり、いろいろなテキスト処理のできるコマンドです。sedには副コマンドがあり、このような形で利用されます。
$ sed -e 'コマンドとパラメータ'
まず、オプションである「-e」はデフォルトです。デフォルトなのでなくても実は動作します。
コマンドには「sコマンド」「dコマンド」「pコマンド」があります。
例:
$ sed -e 's/xxx/XXX/g' # sコマンド:後ろにはパラメータがきます
$ sed -e '30,31d' # dコマンド:前にアドレス(行数)がきます$ sed -n -e '30p' # pコマンド:前にアドレス(行数)がきます
sコマンド
sコマンドは正規表現で置換処理をします。
$ sed -e 's/abc/ABC/g'
abcという文字列をABCという文字列に置換します。最後の'g'の意味は全てにabcの文字列に対して行わせることです。もし'g'がなければ、各行の最初のabcのみが置換され、後ろのabcは変換されずにそのままになります。
例えばある文字列の中で特定のパタンの(固定でない)文字列を削除した文字列を生成して比較対象から外したい場合は以下のようにすると、
※バージョン違いは比較対象から外して比較したい場合
$ cat "TEST ver1.1.0" | sed -E 's/TEST ver(.....)/TEST ver/g'
TEST ver
後ろのバージョン情報が抜け落ちますので、その部分だけ抜きの比較が可能になります。
※-eを-Eにした理由は、デフォルトの基本正規表現から拡張正規表現に変えるオプションです。基本正規表現では、'('や')'を使用するためには文字の前に'\'を入れないといけないのです。
※括弧内に囲まれた5個の点は、どんな文字で5文字までマッチさせてそれを置換する意味です。
後、sコマンドの前に数字を書くと指定された行のみが置換を行う対象となります。
$ sed 3s/abc/ABC/g # 3行目だけを置換する
$ sed 3,5s/abc/ABC/g # 3行目~5行目だけを置換する
$ sed 3,$s/abc/ABC/g # 3行目~最終行まで置換する
大文字小文字を変換するには
$ sed -E 's/(.*)/\U\1/'
小文字大文字を変換するには
$ sed -E 's/(.*)/\L\1/'
dコマンド
dコマンドは行を削除します。
$ sed '1,3d' # 1行目~3行目を削除して4行目から出力
以外に便利な機能として'!'をつけることで必要な箇所以外全部削除できます。
$ sed '3!d' # 3行目のみ出力=3行目以外全部削除
pコマンド
指定した行を出力する。通常は -n オプションと組み合わせて使います。
sedコマンドでは処理結果をデフォルトで出力しますが、 -n オプションを付けると
デフォルトの出力がされなくなり、 pコマンドの出力のみになります。
$ sed -n -e 3p # 先頭から3行目のみが出力されます。
以下と同じ結果になりますが、他全部を消すより、指定行のみを出力させることが
効率はいいと思います。
$ sed '3!d' # 3行目のみ出力=3行目以外全部削除
$ sed -n -e '$p' # 最後の行のみ表示されます。
$ sed -n -e 2,5p # 2行目から5行目まで出力されます。$ sed -n -e 1~3p # 先頭から1行目から2行置きで出力されます。奇数行のみ。
$ sed -n -e 1~5p # 1行目、6行目、…+5行ずつで出力されるイメージです。
番外編
[test.txt]
aaa
bbbccc
ddd
検索結果が行数と一緒に表示される
> grep -e ccc -n test.txt
3:ccc
行番号だけ取得
> grep -e ccc -n test.txt | sed -e 's/:.*//g'
3
オプション
- -e script:処理内容を指定する。
- -E:拡張正規表現を使う。基本正規表現と拡張正規表現を参照。
- -i, --in-place:結果を標準出力せずにファイルを置き換える。
- -n:修理する各行の自動出力をしない。 -p コマンドによる出力のみになる。
- -r:拡張正規表現を使う。-E と同じなのかな(?)
- --help:ヘルプを表示
- --version:バージョンを表示
記事作成:2018.10.27
記事更新:2018.10.27
この記事がより多くの方の目に触れるようブログランキングに参加しました。
応援よろしくお願いします。
↓↓↓↓↓↓↓↓↓↓↓↓↓