sed コマンドで改行コードを置換したり削除したい
GNU sed – GNU Project – Free Software Foundation
sed
コマンドはテキストをフィルタリングしたり操作するためのコマンドです。テキストファイルから入力を受け取り、何らかの操作を実行し結果を出力します。直接操作結果でファイル内容を変更することも可能です。
今回は改行コード(CRLF
, LF
)を置換するために sed
コマンドを使う方法をまとめます。
結論
先に結論だけ知りたい人は以下をどうぞ。
- CRLF -> LF
sed -i 's/\r//g' input.txt
sed -i -z 's/\r\n/\n/g' input.txt
- LF -> CRLF
sed -i 's/$/\r/g' input.txt
sed -i -z 's/\n/\r\n/g' input.txt
- CRLF, LF を削除
sed -i -z 's/\n//g' input.txt
sed -i -z 's/\r\n//g' input.txt
input.txt
の改行コードを置換したり削除したりします。
ではコードの解説です。
sed コマンドの使い方とオプション
まず sed
の基本的な使い方です。
sed [置換内容] [対象ファイルパス]
置換結果は標準出力に出力されます。
'hello' を 'world' に置換する例です。
$ echo 'hello hello' > input.txt
$ cat input.txt
hello hello
$ sed 's/hello/world/' input.txt
world hello
置換内容
は 's/[置換前]/[置換後]/'
で指定します。見つかった最初の1つだけ置換されます。すべての置換対象を置換するには以下のように g
を付けます。
$ sed 's/hello/world/g' input.txt
world world
複数の置換をまとめて実行する
;
で区切って複数の置換内容を指定することができます。
$ echo 'hello world' > input.txt
$ cat input.txt
hello world
$ sed 's/hello/XXX/; s/world/YYY/' input.txt
XXX YYY
正規表現を使う
置換対象を正規表現で指定することも可能です。例えば数字をすべて'x'に置換する例です。
$ echo 'hello123 world456 789' > input.txt
$ sed -e 's/[0-9]/x/g' input.txt
helloxxx worldxxx xxx
-i: ファイルを直接操作する
-i
を指定すると、ファイルに対して直接操作内容で更新します。
$ echo 'hello hello' > input.txt
$ sed -i 's/hello/world/g' input.txt
$ cat input.txt
world world
sed
コマンド実行後、対象ファイルの input.txt
が更新されていることがわかります。
-z: 改行コード(\n) を操作できるようにする
sed
コマンドはデフォルトだと改行(\n
)単位で分割されたテキスト(ストリーム)に対して操作を行います。したがってこのままでは改行コード(\n
)を直接置換したり削除することができません。
-z
(--null-data
) オプションは分割をNULL文字で行うようにします。これで \n
を置換できるようになります。
$ echo -e '1\n2\n3' > input.txt
# -z なしだと \n を置換できない
$ sed 's/\n//g' input.txt
1
2
3
# -z ありだと \n を置換できる
$ sed -z 's/\n//g; s/$/\n/' input.txt
123
テキストから改行文字(\n
)をすべて削除されていることがわかります。's/\n//g;'
だと末尾の改行も削除されるので、末尾に改行を追加するために 's/$/\n/'
を追加しています。
もし明示的に改行コード \n
を対象に操作したい場合は -z
を使いましょう。ただし改行コードの変換は後述の方法で -z
なしでも実行できます。
以上のオプションや使い方を組み合わせて改行コードを置換する方法をまとめます。
sed コマンドと正規表現で CRLF を LF に置換する
# CRLFのテキストファイルを用意
$ echo -en '1\r\n2\r\n3\r\n' > input.txt
# CRLF(\r\n) を LF(\n) に置換する
$ sed -i 's/\r//g' input.txt
CRLF を LF に置換したい場合は単純に CR
を削除すればよいので、空文字に置換しましょう。
実行後にファイルを cat
コマンドで改行コードを表示してみてみましょう。
$ cat -e input.txt
1$
2$
3$
^M
が CR
(\r
) で $
が LF
(\n
) です。全行の改行コードが \n
になっています。
sed コマンドと正規表現で LF を CRLF に置換する
# LFのテキストファイルを用意
$ echo -en '1\n2\n3\n' > input.txt
# LF(\n) を CRLF(\r\n) に置換する
$ sed -i 's/$/\r/g' input.txt
LF を CRLF に変換したい場合、正規表現の $
を使って行末尾に \r
を挿入します。これで \r\n
になります。デフォルトで区切りが \n
になるので末尾に \r
を挿入すると、\r\n
になるという仕組みです。
実行後にファイルを cat
コマンドで改行コードを表示してみてみましょう。
$ cat -e input.txt
1^M$
2^M$
3^M$
^M
が CR
(\r
) で $
が LF
(\n
) です。全行の改行コードが \r\n
になっています。
CRLF と LF が混在するテキストを CRLF に統一する
もし CRLF と LF が混在するようなテキストを対象に CRLF に統一したい場合、上記のやり方ではおかしくなります。
# CRLF と LF が混在するテキスト
$ echo -en '1\r\n2\n3\n' > input.txt
# \r を挿入するとおかしくなる
$ sed -i 's/$/\r/g' input.txt
$ cat -e input.txt
1^M^M$
2^M$
3^M$
このような場合には「直前に CR
がない LF
」を「CRLF
」に置換するという操作を実行します。いわゆる「否定的後読み」を実行しなければなりませんが、sed
の正規表現では先読みや後読みができないようです。
したがって単純に2度 sed
を実行すればよいです。
$ sed -i -z 's/\n/\r\n/g' input.txt && sed -i 's/\r\r/\r/g' input.txt
$ cat -e input.txt
1^M$
2^M$
3^M$
もっといい方法があるかもしれませんが、とりあえずこれでうまくいきそうです。
find + sed で複数ファイルの改行コードをまとめて置換する
ここまでで任意のファイル内の改行コードを置換することが可能になりました。最後に find
コマンドで検索して得られたファイルに対して改行コード変換を実行する例です。
├── a.txt
├── b.txt
└── my_script.sh
このようなディレクトリ構造を用意します。ファイルの内容は以下のようになっています。
$ find . -type f -name '*.txt' -exec cat -e {} \;
a.txt: LINE-1^M$
a.txt: LINE-2^M$
b.txt: LINE-1^M$
b.txt: LINE-2^M$
a.txt
も b.txt
も ^M$
があるので 改行コード CRLF(\r\n
) になっています。これをまとめて置換します。
実行は 同じく find -exec
を使います。
[Linux] find コマンドでファイルやディレクトリを検索する │ Web備忘録
# .txt ファイルの CR を全部削除(CRLF -> LF)
$ sed -i 's/\r//g' *.txt
# 結果確認
$ find . -type f -name '*.txt' -exec cat -e {} \;
a.txt: LINE-1$
a.txt: LINE-2$
b.txt: LINE-1$
b.txt: LINE-2$
^M
(\r
) がなくなっています。うまく置換できました。改行の削除なども同じく -exec
で実行するコマンドを変更して実行してください。
以上。
コメントを書く