僕はただ改行を削除したいだけなんだ。。。
POSIX準拠の範囲でなんとかしたい。
単純に全改行を削除
1文字の削除にはtr
コマンドを使いましょ。
(infile
は改行を削除したいファイル)
tr -d '\n' < infile
指定パターンにマッチした行のみ改行を削除
改行文字の削除の例でこんな感じのを見かける。 GNU系とBSD系で書き方が違う時点で目的とはかけ離れるんだけど、何かヒントになりそう。
sed ':loop; N; $!b loop; s/\n//g' < infile # GNU版sed sed -e ':loop' -e 'N; $!b loop' -e 's/\n//g' < infile # BSD, Mac
sed
は通常\n
がマッチしないのだけど、上のやり方だとマッチするっぽい。
sedの挙動解説
通常はsed
のパターン検索では\n
にマッチしない。
それはなぜか。
sed
内部では指定の操作を行う際に、以下のような動作をするらしい。
1. 次の入力行を、改行文字を削除して、パターンスペースというバッファに格納
2. そのパターンスペースに対してマッチングやら指定の操作(置換や削除など)をする
3. パターンスペースの内容の末尾に改行を追加して出力
つまり、従来はパターンスペースという検索対象バッファに\n
がそもそも含まれていないため、'\n'を指定してもマッチしない。
それがNコマンドを使うと、検索対象バッファに改行文字+次の1行を追加する。
それでめでたく'\n'がマッチするということらしい。
詳細はこちらのサイトさんを参照されたし。
普段から\n
くらいマッチしてほしいんだけど。
なんだろう、sed
が開発された頃の環境の都合があったのかな。。。
コード解説
先程のコード再掲。
sed -e ':loop' -e 'N; $!b loop' -e 's/\n//g' < infile # BSD, Mac
最初これ見てもサッパリだったのだけれども、複数行に分けてみる。
:loop # ラベル定義 N # パターンスペースに改行文字+次の1行を追加 $!b loop # 最終行($)じゃなければ(!)、:loopへジャンプ(b loop) s/\n//g # `\n`を全て削除
Nよってパターンスペースに改行文字が入る。
その後、パターン検索すれば\n
もめでたくマッチするので、削除や置換が可能というカラクリ。
でも、行頭という意味での^
が使えない。
代わりに`[^\n]*'を指定しても挙動がおかしい。なんでじゃ。
妥協策(行末パターンを指定して削除)
上の削除例とこちらのサイトさんを参考に下のような妥協案。
引数で指定した行末パターン(/xxxx$/
)にマッチする行だけ改行文字を削除する。
#!/bin/sh PATTERN=$1 sed -e ':loop' \ -e '/'"$PATTERN"'/N;' \ -e 's/\([^\n]*\)\n\([^\n]*\)$/\1\2/' \ -e '/'"$PATTERN"'$/b loop' \
うーん、あと一歩最後の行を工夫すればできそうな気がする。。。
あるパターンにはさまれた複数行を1行にまとめる
下みたいにユニークな文字(<start>
,<end>
)で挟まれている複数行を、1行にまとめるパターンならいける。
<start>
と<end>
は他に出てこない文字列ならなんでもいい。
> cat infile へっだ <start> まとめたい 内容の 行たち 1 <end> ごみ <start> まとめたい 内容の 行たち 2 <end> ふった
Step 1. sed
でまとめたい範囲だけを抜き出す。
> sed -n '/<start>/,/<end>/p' < infile <start> まとめたい 内容の 行たち 1 <end> <start> まとめたい 内容の 行たち 2 <end>
Step 2. grep -v
で<start>
行を省いて、<end>
を置換可能な一文字にする。
> sed -n '/<start>/,/<end>/p' < infile | grep -v "<start>" | sed 's/<end>/>/g' まとめたい 内容の 行たち 1 > まとめたい 内容の 行たち 2 >
Step 3. 改行を一旦消して、<end>
を置換した文字を改行に置換
> sed -n '/<start>/,/<end>/p' < infile | grep -v "<start>" | sed 's/<end>/>/g' | tr -d '\n' | tr '>' '\n' まとめたい内容の行たち1 まとめたい内容の行たち2
さいごに
いやぁもっと簡単な方法あるでしょ。。。
みんなどうやってんだ。
最終兵器awk
しかないのかしら。