ファイルや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を使った手もあるでしょうが、長くなるので、シェルな雰囲気ではなく、やめておきました。