はじめに
grep -f
を使っていて、「検索文字列が末尾にある行」しかヒットしない※という事が起こった場合
改行コードが原因かもしれません。
→ sed
やecho
で改行コードを直してやればうまくいくようになります、という内容です。
(※Windowsの場合。Macだと違う挙動かも?)
※ ファイル内の各行を検索文字列に使用したい場合には、grep -f
や、while
文で1行ずつ読み込む(while read line
)などの方法が使えます。
・grep -f オプション
grep -f [検索文字列ファイル] [検索対象ファイル]
・while文を利用
while read line do grep "$line" [検索対象ファイル] done < [検索文字列ファイル]
(※この書き方の場合、**[検索文字列ファイル]**の最終行で改行している必要あり(じゃないと最終行だけ読み込まれない)。複数条件に該当する行は何度も出現してしまうので注意)
起こっている事
下記のように、ファイル内の各行で検索を掛けたときに、「検索文字列が末尾にある行」しか選択されないという現象が起こることがあります(というか起こった)。
AAA
BBB
AAAxxxxxxxx
xxxxxAAAxxx
xxxxxxxxAAA
BBBx BBB xx
xxxxxxxxBBB
xxAAAxxBBBx
xxxCCCxxxxx
$ grep -f file.txt test.txt
xxxxxxxxAAA
xxxxxxxxBBB
$ while read line
> do
> grep "$line" test.txt
> done < file.txt
xxxxxxxxAAA
xxxxxxxxBBB
対処法の例
1. sedコマンド
なんでもいいので適当にsed
を使って**[検索文字列ファイル]**を作り直すことで、うまく動くようになります。
$ sed 's/^//' file.txt > file2.txt
$ grep -f file2.txt test.txt
AAAxxxxxxxx
xxxxxAAAxxx
xxxxxxxxAAA
BBBx BBB xx
xxxxxxxxBBB
xxAAAxxBBBx
2. echoコマンド
while
文の方では、echo
で$line
を読み直す方法でも、うまくいきます。
$ while read line
> do
> grep `echo $line` test.txt
> done < file.txt
AAAxxxxxxxx
xxxxxAAAxxx
xxxxxxxxAAA
xxAAAxxBBBx ##
BBBx BBB xx
xxxxxxxxBBB
xxAAAxxBBBx ## 複数条件に該当する場合は何度も出現する
その他の対処法
その他の改行コード変換方法の例
・改行コードの変換
解説
今回の挙動の原因は、WindowsとUnixで改行コードが異なることでした。
つまりWindows上で作成したファイルを検索文字列として利用すると、\r の部分が検索の妨げになる("検索文字列 + \r"を探す)ため、末尾に検索文字列がある場合にしかヒットしなかったというわけです。
(実際、file.txtの2行目で改行をなくすと、"BBB"の方は正常に検索されるようになります。また逆にtest.txtにsed
を実行して、改行コードをCRLFからLFに直すと何も出力されなくなります)
OS | 改行コード | 「od -c」での見え方 |
---|---|---|
Unix | LF | \n |
Mac(OSX) | LF | \n |
Mac(OS9) | CR | \r |
Windows | CR+LF | \r\n |
引用:改行コードの確認 |
$ grep -f file.txt test.txt
xxxxxxxxAAA
BBBx BBB xx
xxxxxxxxBBB
xxAAAxxBBBx
$ sed 's/^//' test.txt > test2.txt
$ grep -f file.txt test2.txt
sed
やecho
を使用すると、改行コードが**CRLF(\r\n)からLF(\n)**に変換されるため、検索がうまくいくようになります。
## ---------------------- sed 前 (CRLF)
$ file file.txt
file.txt: ASCII text, with CRLF line terminators
$ od -c file.txt
0000000 A A A \r \n B B B \r \n
0000012
## ---------------------- sed 後 (LF)
$ file file2.txt
file2.txt: ASCII text
$ od -c file2.txt
0000000 A A A \n B B B \n
0000010
$ cat hoge.txt
hoge
$ while read line
> do
> echo `echo $line` > hoge2.txt
> done < hoge.txt
## ---------------------- echo 前 (CRLF)
$ od -c hoge.txt
0000000 h o g e \r \n
0000006
## ---------------------- echo 後 (LF)
$ od -c hoge2.txt
0000000 h o g e \n
0000005
参考:
・【 sed 】コマンド(基礎編)――テキストファイルを編集する
・Windowsのsedで置換して変わった改行コードをLFからCRLFに戻す方法
なお、Macの場合、以前のMacOSではCRが採用されていましたが、MacOSX以降はUnix系OSと同じLFとなっているとのことです。