web-dev-qa-db-ja.com

sedを使用して複雑な文字列を検索および置換する(できれば正規表現を使用)

次の内容のファイルがあります。

<username><![CDATA[name]]></username>
<password><![CDATA[password]]></password>
<dbname><![CDATA[name]]></dbname>

1行目の「名前」を「something」に、2行目の「パスワード」を「somethingelse」に、3行目の「name」を「somethingdifferent」に変更するスクリプトを作成する必要があります。ファイル内で発生するこれらの順序に依存することはできないため、「name」の最初の出現を「something」で、2番目の出現の「name」を「somethingdifferent」で単純に置き換えることはできません。実際に周囲の文字列を検索して、正しいものを見つけて置き換えていることを確認する必要があります。

これまでのところ、私はこのコマンドを試して、最初の「名前」の出現を見つけて置き換えました。

sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml

しかし、それは機能していないので、これらのキャラクターの一部はエスケープする必要があるかもしれないと思います。

理想的には、正規表現を使用して2つの「ユーザー名」の出現を一致させ、「名前」のみを置き換えることができるようにしたいと思います。このようなものですが、sed

<username>.+?(name).+?</username>

括弧内の内容を「何か」に置き換えます。

これは可能ですか?

102
Harry Muscle
sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml

これはあなたが探しているものだと思います。

説明:

  • 最初の部分の括弧は、2番目の部分で再利用できるグループ(実際には文字列)を定義します
  • 2番目の部分の\1\2などは、最初の部分でキャプチャされたi番目のグループへの参照です(番号は1から始まります)。
  • -Eは、拡張正規表現を有効にします(+およびグループ化に必要)。
187
lgeorget
sed -e '/username/s/CDATA\[name\]/CDATA\[something\]/' \
-e '/password/s/CDATA\[password\]/CDATA\[somethingelse\]/' \
-e '/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/' file.txt

/username/の前のsは、文字列「username」を含む行でのみ機能するようにsedに指示します。

16
evilsoup

sedが難しい要件でない場合は、代わりに専用ツールを使用することをお勧めします。

ファイルが有効なXMLである場合(これらの3つのXMLに見えるタグだけではない)、 XMLStarlet を使用できます。

xml ed -P -O -L \
  -u '//username/text()' -v 'something' \
  -u '//password/text()' -v 'somethingelse' \
  -u '//dbname/text()' -v 'somethingdifferent' file.xml

上記は、正規表現では解決が難しい状況でも機能します。

  • 現在の値を指定せずにタグの値を置き換えることができます。
  • エスケープされていて、CDATAで囲まれていない場合でも、値を置き換えることができます。
  • タグに属性がある場合でも値を置き換えることができます。
  • 同じ名前のタグが複数ある場合、タグの出現箇所のみを簡単に置き換えることができます。
  • 変更されたXMLをインデントしてフォーマットできます。

上記の簡単なデモ:

bash-4.2$ cat file.xml
<sith>
<master>
<username><![CDATA[name]]></username>
</master>
<apprentice>
<username><![CDATA[name]]></username>
<password>password</password>
<dbname foo="bar"><![CDATA[name]]></dbname>
</apprentice>
</sith>

bash-4.2$ xml ed -O -u '//apprentice/username/text()' -v 'something' -u '//password/text()' -v 'somethingelse' -u '//dbname/text()' -v 'somethingdifferent' file.xml
<sith>
  <master>
    <username><![CDATA[name]]></username>
  </master>
  <apprentice>
    <username><![CDATA[something]]></username>
    <password>somethingelse</password>
    <dbname foo="bar"><![CDATA[somethingdifferent]]></dbname>
  </apprentice>
</sith>
7
manatwork
$ sed -e '1s/name/something/2' \
      -e '3s/name/somethingdifferent/2' \
      -e 's/password/somethingelse/2' sample.xml

行番号を示す「s」の前の数字のように、アドレスを使用できます。

また、最後の数字は、最初の一致を置き換えるのではなく、2番目の一致を置き換えるようにsedに指示します。

5
A. Wench

sコマンドの正規表現部分では\[.*^$/を、置換部分では\&/を引用符で囲み、改行を追加する必要があります。正規表現は 基本正規表現 であり、さらにsコマンドの区切り文字を引用符で囲む必要があります。

/を引用する必要がないように、別の区切り文字を選択できます。代わりにその文字を引用符で囲む必要がありますが、通常、区切り文字を変更するポイントは、置換するテキストまたは置換テキストのいずれにもないものを選択することです。

sed -e 's~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~'

グループを使用して、置換テキストの一部を繰り返さないようにし、これらの部分のバリエーションに対応できます。

sed -e 's~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~'

sed -e 's~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~'

「名前」の単語を「何か」の単語に置き換えるには、次を使用します。

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml

これにより、指定されたWordのすべての出現箇所が置き換えられます。

これまでのところ、すべてが標準出力に出力されています。

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml

変更を別のファイルに保存します。

1
slackmart
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...

    -r, --regexp-extended
             use extended regular expressions in the script.

プロパティファイルの値を置き換える

sed -i -r 's/MAIL\=(.+)/MAIL\[email protected]/' etc/service.properties 
0
alfiogang