sed:見つかった場合は行を置換する方法、見つからない場合はファイルの最後に追加する方法


34

コメント(#で始まる)とVARIABLE = value行のみを含む単一の入力ファイルで、単一の変数の値が見つかった場合は置換し、見つからない場合はファイルの末尾にペアを追加できますか?

私の現在の方法は、最初のパスでそれを削除してから、2回目のパスでファイルの最後に追加することで機能しますが、このメソッドは行の順序を乱します(また2つの異なるコマンドです):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

とにかくこれを行うにはありますか?単一のsed行で行の順序を維持しますか?他のユーティリティ(awk、...)がこれを行う場合、sedを引き継ぎます。

回答:


29

実際には非常に簡単sedです。行が一致する場合、それをh古いスペースにコピーするだけでs、値を置き換えることができます。lat行で
、ホールドスペースとパターンスペースを変更し、後者が空かどうかを確認します。空でない場合は、すでに置換が行われているので何もする必要がないことを意味します。空の場合は、一致が見つからなかったことを意味するため、パターンスペースを目的のvariable = valueに置き換えてから、保持バッファーの現在の行に追加します。最後に、再び変更します。$xx

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

上記はgnu sed構文です。ポータブル:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile

1
newvalue変数に格納されている場合、これはどのようになりますか?
user1810087

sed "/^${varName}=/{h;s/=.*/=${varValue}/};\${x;/^$/{s//${varName}=${varValue}/;H};x}" ${VARFILE}変数、二重引用符を使用して変数の置換を可能にし$、この時点で置換ではないものをエスケープします。さらに、値が次の場所に保存されている場合$$varNamesed "/^${varName}=/{h;s/=.*/=${!varName}/};\${x;/^$/{s//${varName}=${!varName}/;H};x}" ${VARFILE}
DarkMukke

1
この解決策は「単純な」笑ではありません。しかし、それは機能します。sedのmanページは、ふるいにかけるのが最も簡単なものではありません。式のすべてのセクションの効果を詳しく説明できれば、非常に役立ちます。)
user2066480

18

これはおそらく短縮できます。これは単一のsedコマンドではなく、grepも使用しますが、これは基本的には必要なもののようです。これは1行で、ファイルをその場で編集します(一時ファイルはありません)。

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file

13

作業が簡単でsedはないので、ここに簡単なアプローチsed hold spaceを示します。に慣れている場合はhold space、don_crisstiアプローチを使用すると、既存の行のすべてを保持する追加の機会が得られますが、これは通常非常にまれです。

このアプローチでは、ドロップする行以外のすべてを印刷し、最後に置換を追加します。

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile

1
このバージョンは、既存の古い値を持つ可能性のあるファイルを更新する場合に非常に役立ちます。
ギエム

3

他の回答に基づいて、あなたがしたいことは、変数がファイルに存在する場合は変数の値を置き換え、そうでない場合はファイルの最後に追加することです(投稿されたsedコマンドがそうではない)これを試すことができます:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file

1

「インプレース編集」は自動ではありませんが、awkでは少し簡単です。

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file

1

grepechoを使用して、空のレコードを作成します。

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

各行はエラーコード0でエスケープします。


追加のコマンドを追加しているときに行の順序が失われ、grepかつecho
-BlakBat

確かに、ファイルの最後に新しいアイテムを挿入しても、古い値を置き換える場合は順序を維持します。
MUYベルギー

0

実際に次のように動作します。選択され sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile た答えでは-iが欠落しています。


3
これは実際にはコメントであり、元の質問に対する答えではありません。投稿者に批評または説明を依頼するには、投稿の下にコメントを残します-自分の投稿にいつでもコメントできます。評価が十分になったら、投稿にコメントできます。コメントするのに50の評判が必要な理由を
DavidPostill

-1

perlとインプレースでこれを行うには、これは私にとってうまくいきます:

grep ^FOOBAR= my.file && perl -i -ple "s/^FOOBAR=.+/FOOBAR=newvalue/g" my.file || echo FOOBAR=newvalue >> my.file

2
この答えは多くの点で悪いです! grepそしてechoperl代わりにすべてを行う代わりにperl?また、>> my.file読み取り中にファイルに書き込み()しますgrep my.fileか?
vladr
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.