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

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

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

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

最初の行の「名前」を「something」、2行目の「password」を「somethingelse」、3行目の「name」を「something other」に変更するスクリプトを作成する必要があります。ファイルの発生順序に依存できないため、「名前」の最初の発生を「何か」に変更し、2番目の「名前」の発生を「何か他のもの」に置き換えることはできません。実際に正しいコンテンツを見つけて交換したことを確認するには、周囲の文字列を検索する必要があります。

これまで、私はこのコマンドを使って「名前」の最初の項目を見つけて置き換えようとしました。

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

しかし、うまくいかないので、いくつかの文字をエスケープする必要があるかもしれません。

理想的には、正規表現を使用して2つの「ユーザー名」を一致させ、「名前」のみを変更できることを願っています。これに似ていますが、以下を使用してくださいsed

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

そして括弧内の内容を「something」に置き換えます。

可能ですか?

答え1

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

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

説明する:

  • 最初の部分の角かっこは、2番目の部分で再利用できるグループ(実際には文字列)を定義します。
  • \12番目の部分の背面は、\2最初の部分でキャプチャされたi番目のグループへの参照です(番号は1から始まります)。
  • -E+拡張正規表現を有効にします(グループ化に必要)。
  • -i「内部」ファイル編集モードを有効にする

答え2

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/の例では、ssedに "username"という文字列を含む行でのみ作業するように指示しました。

答え3

難しい要件ではない場合は、sed特別なツールを使用するのが最善です。

ファイルが有効なXML(XMLのように見える3つのタグだけではありません)の場合は、次のものを使用できます。XMLスター:

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>

答え4

\[.*^$/正規表現部分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~'

関連情報