ファイルやls
のような行単位の出力に対し、先頭または末尾に文字列を加えるUnixな方法がいくつかあるか、考えてみました。
次の3行のファイルを考えます(以下、file=requirements.txt
)。
pykakasi
opencv-python
requests
これら行にpip show␣
という文字列を先頭に加えます(以下、prepend="pip show␣"
)。
pip show pykakasi
pip show opencv-python
pip show requests
先頭も末尾もコード的にはたいして変わりません。後尾に加える文字列は$append
にあるとします。
sed
行単位の編集ならやはりsed
が王道です。行頭を正規表現の^
で示すのがポイントです。
$ cat $file | sed "s/^/$prepend/"
末尾なら$
です。
$ cat $file | sed "s/$/$append/"
awk
あるいはawk
(3人ともご存命のようでなによりです)。行単位の処理の{}
の中でシェル変数を参照するのに-v
オプションを併用するのがポイントです。
$ cat $file | awk -v prepend="$prepend" '{print prepend$0}'
末尾にするならprint
の引数を入れ替えます。
$ cat $file | awk -v append="$append" '{print $0append}'
while + echo
行単位でループしてecho
で出力をいじるストレートな解法が、実は一番わかりやすいかもしれません。
$ cat $file | while read line; do echo $prepend$line; done
こちらも、末尾にするならecho
の引数の順序を入れ替えます。
while + printf
echo
の代わりにprintf
を使う手もあります。上記とたいして変わりませんが。
$ cat $file | while read line; do printf "$prepend%s\n" $line; done
for + echo
while
ではなくfor
を使う手もあります。この場合、コマンド先頭でデータを用意してきたこれまでのパターンとはちょっと違うのが(本人的には)マイナス点です。
for line in `cat $file`; do echo $prepend$line; done
xargs
行を行として出力するのに、複数行を1行にまとめるxargs
をわざわざ使うのは病気と言わざるを得ませんが、まぁ、やってできないことはないわけです。
$ cat $file | xargs -L 1 echo $prepend
末尾にするのはecho
ではしんどいので、printf
を使います。
$ cat $file | xargs -L 1 printf "%s$append\n"
jq
さらに病が高じると、JSONパーザーのjq
なんかでやってしまいたくなるわけです。
入力がJSONではないという禁忌を回避しなければならないため、オプションに-r
(--raw
)、-s
(--slurp
)、-R
(--raw-input
)を併用します。また、シェル変数を内部で展開させるため、--arg
オプションでjq
の変数に置き換えるというめったに使わない技まで使います。フィルタを二重引用符でくくれればそんな手間はかからないのですが、フィルタ内部の二重引用符をいちいちエスケープすると読みにくくていけません。
$ cat $file | jq --arg prepend "$prepend" -rsR 'rtrimstr("\n") | split("\n")[] | $prepend + .'
末尾版は、ここでも最後の出力部分の順序を入れ替えるだけです。
jq
の書籍は2冊出ていますが(『jqハンドブック』と『jqクックブック』)、どちらもこんな技は書いてないですね。
perl
最近では使い手も減ってきましたが、こういうワンライナーはやっぱりperl
です。
while(<FILE>)
を仮定する-n
オプションと、それに加えて$_
を自動で出力する-p
オプションは相変わらず強力です(これを見せるほど追い詰められたのは80年ぶりです)。まぁ、sed
の解法と変わらないですが。
$ cat $file | perl -pe "s/^/$prepend/"
sed
同様、末尾なら$
です。
おわりに
まだまだ解法があるような気もしますが、まぁ、こんなもんです。NodeやPythonを使った手もあるでしょうが、長くなるので、シェルな雰囲気ではなく、やめておきました。