やりたいこと
次の2つのファイル(文字列)がある
hogehoge hugahuga
hogahoga hugehuge
AAAAAA
BBBBBB
CCCCCC
FLAG
DDDDDD
EEEEEE
このときにtarget
のFLAG
の部分をsource
の内容で置き換えたい。
つまり、
AAAAAA
BBBBBB
CCCCCC
hogehoge hugahuga
hogahoga hugehuge
DDDDDD
EEEEEE
こんな感じ。
これが少し面倒で、詰まったので書き残す。
結論
これでOK
str=`cat source | # 挿入する文字列を読み込み
sed -r 's/$/\\\\n/' | # 各行の末端に改行文字を入れる
sed -r '$s/\\\\n//' | # 最終行の改行文字に関しては必要ないので取り除く
while IFS= read -r line # 1行ずつ読み込み
do
echo -n "$line" # echo -nで各行を改行しないで出力->1行にまとめる
done`
cat target |
sed -r "/^FLAG$/s/.+/$str/" # sedで必要な部分に挿入(置換)
解説1/3
まずは次のやり方が思いつくと思う。
str=`cat source`
cat target | sed -r "/^FLAG$/s/.+/$str/"
しかしこれでは上手くいかない。
というのも、どうもsedは置換後の文字列($str
)が複数行だと上手く置換してくれずにエラーを吐いてしまう
sed: -e expression #1, char 28: unterminated `s' command
つまり$str
が複数行で無ければいい。
解説2/3
source
の内容は複数行なのに$str
は複数行ではいけない。
矛盾しているようだが、次のsed
の仕様を使えばいける。
$ cat target | sed -r '/^FLAG$/s/.+/hogehoge hugahuga\nhogahoga hugehuge/'
AAAAAA
BBBBBB
CCCCCC
hogehoge hugahuga
hogahoga hugehuge
DDDDDD
EEEEEE
つまり改行文字\n
は有効なのである。
従って、source
の内容を1行に変換しつつ、元の改行位置に\n
を入れた$str
を用意すればOK
解説3/3
複数行を1行にまとめる方法に関しては、固定行数であればいくつかあるが任意の行数となるとなかなか難しい。今回は次の方法を採った。
$ cat source | while IFS= read -r line ; do echo -n "$line" ; done
hogehoge hugahugahogahoga hugehuge
つまりwhile read
で行ごとに読み込み、echo -n
で各行を改行なしで出力している。
IFS=
と-r
に関しては、デフォルトだと先頭・末端のスペースやバックスラッシュを取り除くwhile read
の仕様対策。
あとは、1行にまとめる前に各行の末端に改行文字\n
を入れておけばOK(エスケープする必要はあるが)