sedを使用して、複雑な文字列を検索および置換します(正規表現を使用することが望ましい)


84

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

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

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

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

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

しかし、それは機能していないので、これらのキャラクターのいくつかはエスケープする必要があるなどと考えています。

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

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

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

これは可能ですか?


2
極端に考案されていない限り、ほとんどすべての正規表現ベースのソリューションは、入力形式が変更されるたびに破損するリスクがあることに注意してください。正規表現は、XML、SGML、または派生物(これは私には見えます)を扱うための貧弱な選択です。
CVn

承認されました!たとえば、XQueryの使用を検討してください:w3schools.com/xquery/default.asp。これは、XMLコンテンツを取得および操作するためのW3C標準です。
lgeorget

回答:


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

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

説明:

  • 最初の部分の括弧は、2番目の部分で再利用できるグループ(実際には文字列)を定義します
  • \1\2など、2番目の部分は、最初の部分でキャプチャされたi番目のグループへの参照です(番号は1から始まります)
  • -E拡張された正規表現を有効にします(+グループ化に必要)。

21
-Eオプションの+1
slackmart

4
という名前のバックアップファイルが残ります(original name) + "-E"
セージボルシュ

4
OSXでは、 'sed:1: "s /(<username>。+)name(。+ ...":REで定義されていない\ 1'を取得します。この質問の正確な例をファイルに貼り付けました。そのファイルでこの回答からコマンドを実行しましたが、OSXの構文は異なるのでしょうか?
-deweydb

1
sedのgnuバージョンは「-E」パラメーターをサポートしていますが、公式ではありません。マンページにも記載されていません。拡張正規表現を使用する場合は、代わりに「-r」パラメーターを使用する必要があります。
イケムクルーガー

3
@deweydb この回答によると、\(andの\)代わりに(andを使用する必要があり)ます。
張バズ

14
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文字列「ユーザ名」を含む行のみの仕事にsedを伝えます。


1
エレガントで効率的で、ケースに完璧にフィットします。+1
lgeorget

6

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

ファイルが(これらの3つのXMLに見えるタグだけでなく)有効な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>

3

コマンドの\[.*^$/正規表現部分と置換部分、および改行で引用符で囲む必要があります。正規表現は基本的な正規表現であり、さらにコマンドの区切り文字を引用符で囲む必要があります。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~'

3
$ sed -e '1s/name/something/2' \
      -e '3s/name/somethingdifferent/2' \
      -e 's/password/somethingelse/2' sample.xml

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

また、最後の数字はsed、最初の一致を置き換えるのではなく、2番目の一致を置き換えることを示しています。


1

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

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

これは、指定された単語のすべての出現を置き換えます。

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

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

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


0
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\=user@mymail.com/' etc/service.properties 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.