sed コマンドで改行コードを置換したり削除する方法

sed コマンドで改行コードを置換したり削除する方法

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$

^MCR(\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$

^MCR(\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.txtb.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 で実行するコマンドを変更して実行してください。

以上。

参考URL

Linuxカテゴリの最新記事