シェルスクリプトで、ファイル内の文字列を次のように置換したいのですが、うまくいきません。
--------
対象ファイル
myfile
置換前
&hogehoge("barbar", $bazbaz);
置換後
&hogehoge("barbar", "$chomechome, $bazbaz");
&hogehoge("qux", $bazbaz);
--------
sedで複数行の置換をするのでは?ということまでは分かり、以下のようなスクリプトを書きましたが、思った通りの挙動になりません。指定したmyfile内の置換前の文字列1行を置換後の2行に変換するためのスクリプトを教えて下さい。
test.sh
--------
#!/bin/sh
LF=$(printf '\\\012_')
LF=${LF%_}
echo "hogehoge\nfoo\nbar" | sed 's/\\n/'"$LF"'/g'
sed -i 's/&hogehoge("barbar", $bazbaz);/&hogehoge("barbar", "$chomechome, $bazbaz");\n &hogehoge("qux", $bazbaz);/g' ./myfile
--------
やりとりから、&hogehoge("barbar", $bazbaz);より &hogehoge と パラメータである "barbar", $bazbazを認識したうえで、2行分の変換結果を得たいとのことと理解しました。
awk の方が汎用性があると思いますが(構文解析も可能ですが)今回は冒頭からsedでの実施を期待されているようでしたので、次のものを作ってみました。
最初の質問内容と、2回目の例題に対応できる仕様になっています。
スクリプトと動作結果を以下に示します。
WSL2上のUbuntu20.04 bash にて動作検証
sed -r 's/\&hogehoge\(([\"|\$|A-z|0-9]+).*[, \t]([\"|\$A-z|0-9]+).*/\&hogehoge(\1,\"\$chromechrome,\2);\n\&hogehoge(\"qux\",\2);/' myfile.txt
結果
&hogehoge("barbar","$chromechrome,$bazbaz);
&hogehoge("qux",$bazbaz);
sed -r 's/\&hogehoge\(([\"|\$|A-z|0-9]+).*[, \t]([\"|\$A-z|0-9]+).*/\&hogehoge(\1,\"\$chromechrome,\2);\n\&hogehoge(\"qux\",\2);/' myfile2.txt
myfile2.txt
if (&aaaaa($chome) ) {
&hogehoge("barbar", $bazbaz);
}
else {
&hogehoge("barbar-to", "$chome, $bazbaz");
}
結果
if (&aaaaa($chome) ) {
&hogehoge("barbar","$chromechrome,$bazbaz);
&hogehoge("qux",$bazbaz);
}
else {
&hogehoge("barbar,"$chromechrome,$bazbaz");
&hogehoge("qux",$bazbaz");
}
sedでの文字列抽出は以下を参考にしました。
awk を使った方が簡単な事例かと思います。
#! /bin/sh cat myfile | gawk ' /&hogehoge\("barbar", \$bazbaz\);/ { print "&hogehoge(\"barbar\", \"$chomechome, $bazbaz\");" print "&hogehoge(\"qux\", $bazbaz);" next } { print $0 } '
質問の文面にある &hogehoge("barbar", $bazbaz); な行を、固定の 2行で出力し、それ以外の行はそのまま出力します。
質問の文面にあるやつを極力活かすなら、以下のような感じになるかと。
#!/bin/sh LF=$(printf '\\\012_') LF=${LF%_} echo "hogehoge\nfoo\nbar" | sed 's/\\n/'"$LF"'/g' sed -i 's/&hogehoge("barbar", \$bazbaz);/\&hogehoge("barbar", "$chomechome, $bazbaz");\n\&hogehoge("qux", $bazbaz);/g' ./myfile
やりとりから、&hogehoge("barbar", $bazbaz);より &hogehoge と パラメータである "barbar", $bazbazを認識したうえで、2行分の変換結果を得たいとのことと理解しました。
awk の方が汎用性があると思いますが(構文解析も可能ですが)今回は冒頭からsedでの実施を期待されているようでしたので、次のものを作ってみました。
最初の質問内容と、2回目の例題に対応できる仕様になっています。
スクリプトと動作結果を以下に示します。
WSL2上のUbuntu20.04 bash にて動作検証
sed -r 's/\&hogehoge\(([\"|\$|A-z|0-9]+).*[, \t]([\"|\$A-z|0-9]+).*/\&hogehoge(\1,\"\$chromechrome,\2);\n\&hogehoge(\"qux\",\2);/' myfile.txt
結果
&hogehoge("barbar","$chromechrome,$bazbaz);
&hogehoge("qux",$bazbaz);
sed -r 's/\&hogehoge\(([\"|\$|A-z|0-9]+).*[, \t]([\"|\$A-z|0-9]+).*/\&hogehoge(\1,\"\$chromechrome,\2);\n\&hogehoge(\"qux\",\2);/' myfile2.txt
myfile2.txt
if (&aaaaa($chome) ) {
&hogehoge("barbar", $bazbaz);
}
else {
&hogehoge("barbar-to", "$chome, $bazbaz");
}
結果
if (&aaaaa($chome) ) {
&hogehoge("barbar","$chromechrome,$bazbaz);
&hogehoge("qux",$bazbaz);
}
else {
&hogehoge("barbar,"$chromechrome,$bazbaz");
&hogehoge("qux",$bazbaz");
}
sedでの文字列抽出は以下を参考にしました。
コメント(5件)
if (&aaaaa($chome) ) {
&hogehoge("barbar", $bazbaz);
}
else {
&hogehoge("barbar-to", "$chome, $bazbaz");
}
また、myfileを上書きする形で処理を終えられればと思います。このような形で上書きできそうと言うことは分かりましたが、ヒットした部分だけ置換する方法が分かりませんでした。お力添え頂けると助かります。
awk '(PROGRAM)' testfile.txt > testfile.tmp && mv testfile.tmp testfile.txt
多分、else 句の中のことを言ってるのだと思いますけれども、
それは、質問の文面からは全く分かりません。
回答では、あくまでも、"&hogehoge("barbar", $bazbaz);" の文字列を置き換えているだけです。
どこが固定で、どこが可変なのかを明示しないと、誰にも回答できません。
前提を欠いておりまして、申し訳ございませんでした。お恥ずかしながら、
実際にスクリプトを走らせた時に質問の内容が正確さに欠けていたことが分かりました。
もし追加でご回答頂けるのであれば、こちらでお願い出来るでしょうか。
前任者が退職しており、一人担当のため誰にも頼れない状況で、こちらの回答は大変助かっています。
置換対象
myfile
やりたいこと
if句の&hogehoge("barbar-to", $bazbaz);の1行を、
&hogehoge("barbar-to","$chromechrome,$bazbaz");と&hogehoge("qux",$bazbaz);の2行に変換したい。
else句は変更しない。
仮面なんとか様のコマンドでは、else句の&hogehoge("barbar-to", "$chome, $bazbaz");が置換されていますが、
そこが置換されなければ、求める結果になります。
置換前
if (&aaaaa($chome) ) {
&hogehoge("barbar-to", $bazbaz);
}
else {
&hogehoge("barbar-to", "$chome, $bazbaz");
}
置換後
if (&aaaaa($chome) ) {
&hogehoge("barbar-to","$chromechrome,$bazbaz");
&hogehoge("qux",$bazbaz);
}
else {
&hogehoge("barbar-to", "$chome, $bazbaz");
}
その場合は、0行目から置換対象の行を絞って置換すればよいので、次の形でよいかと。
sed -r '0,/\&hogehoge\(/ s/\&hogehoge\(([\"|\$|A-z|0-9]+).*[, \t]([\"|\$A-z|0-9]+).*/\&hogehoge(\1,\"\$chromechrome,\2);\n\&hogehoge(\"qux\",\2);/' myfile.txt
結果
if (&aaaaa($chome) ) {
&hogehoge("barbar,"$chromechrome,$bazbaz);
&hogehoge("qux",$bazbaz);
}
else {
&hogehoge("barbar-to", "$chome, $bazbaz");
}
参考にさせてもらった記事
https://www.greptips.com/posts/1235/#:~:text=%E4%B8%80%E7%95%AA%E3%81%AF%E3%81%98%E3%82%81%E3%81%A0%E3%81%91%E7%BD%AE%E6%8F%9B,%E7%BD%AE%E6%8F%9B%E5%BE%8C%2F'%20%E3%81%A8%E3%81%99%E3%82%8B%E3%80%82
ありがとうございます。ほぼ思っているとおりの結果になりました。
ただ、if句のbarbar-toは置換後もbarbar-toのままで、barbarに置換されるとまずいため、頂いたスクリプトを元に修正しました。
if (&aaaaa($chome) ) {
&hogehoge("barbar-to","$chromechrome,$bazbaz");
&hogehoge("qux",$bazbaz);
}
else {
&hogehoge("barbar-to", "$chome, $bazbaz");
}
a-kuma3もご協力下さいましてありがとうございました。