sed または perl を使用してファイル内のすべての一致を列挙します。

sed または perl を使用してファイル内のすべての一致を列挙します。

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

:~$ cat test
        <Set Id="16">
                <Ver="44" Sniff="no" B2vpnId="" Collision="no">
        </Set>        
        <Set Id="17">
                <Ver="22" Sniff="no" B2vpnId="" Collision="no">
        </Set>

ファイル内のB2vpnId値を増やす必要がありますが、出力でこの変更を取得する方法が見つかりました。

:~$ i=1;while read -r line; do echo "$line"|sed -e 's/B2vpnId=""/B2vpnId="'"vpn$i"'"/';((i+=1));done <<< "$(cat test|grep B2vpnId=)"
<Ver="44" Sniff="no" B2vpnId="vpn1" Collision="no">
<Ver="22" Sniff="no" B2vpnId="vpn2" Collision="no">

ただし、他の構成行が多いため、ファイル自体でこれらの変更を行う必要がありますが、これを行う方法を見つけることができません。

答え1

使用されたperl正規表現修飾子は、/e演算子の代替部分をs/search/replace/Perlコードとして評価します。

$ perl -p -e 's/B2vpnId=""/sprintf "B2vpnId=\"vpn%i\"", ++$i/e' test 
        <Set Id="16">
                <Ver="44" Sniff="no" B2vpnId="vpn1" Collision="no">
        </Set>
        <Set Id="17">
                <Ver="22" Sniff="no" B2vpnId="vpn2" Collision="no">
        </Set>

標準出力に出力するのではなく、ディスク上のファイルを変更するには、Perlの-iオプションを使用します。

$i複数のファイルを処理し、各ファイルの後にカウンタ()をリセットするには、スクリプトの; $i=0 if eof最後に次を追加します。

$ cp test test2
$ perl -p -e 's/B2vpnId=""/sprintf "B2vpnId=\"vpn%i\"", ++$i/e; $i=0 if eof' test test2
        <Set Id="16">
                <Ver="44" Sniff="no" B2vpnId="vpn1" Collision="no">
        </Set>
        <Set Id="17">
                <Ver="22" Sniff="no" B2vpnId="vpn2" Collision="no">
        </Set>

        <Set Id="16">
                <Ver="44" Sniff="no" B2vpnId="vpn1" Collision="no">
        </Set>
        <Set Id="17">
                <Ver="22" Sniff="no" B2vpnId="vpn2" Collision="no">
        </Set>

そのステートメントがない場合、$i=0 if eofテストされた2番目のコピーのB2vpnIDは「vpn3」と「vpn4」です。

ただし、カウンターが別の数字(10など)で始まるようにするには、BEGIN { $i=9 };スクリプトの先頭に追加します。 BEGINブロックは入力を読み取る前に一度だけ実行されます。スクリプトの残りの部分は、各入力行に対して一度だけ繰り返し実行されます。しかし、一度だけ実行される別の種類のコードブロックがありますman perlsyn

ブロックの前にコンパイルフェーズキーワード(たとえば、、、、など)がある場合、BEGINそのブロックはENDその実行フェーズでのみ実行されます。詳細は参照してください。INITCHECKUNITCHECKperlmod

$i増えることを覚えておいてください今後毎回使用される(スクリプトがusing++$iの代わりに使用され、$i++毎回使用されるたびに値が増加するため)、開始数字から1を減算します。

また、たとえばBEGINブロックを使用している場合は、$i=0 if eofBEGINブロックと一致するように変更する必要があります。$i=9 if eof

$ perl -p -e 'BEGIN { $i=9 };
              s/B2vpnId=""/sprintf "B2vpnId=\"vpn%i\"", ++$i/e;
              $i=9 if eof' test 

あるいは、BEGINブロックを使用して$ iを初期化するので、$ iを後で増やすことができます(そして別の変数を開始値として使用するので、1つの場所でのみ変更できます)。

$ perl -p -e 'BEGIN { $start = 10; $i = $start };
              s/B2vpnId=""/sprintf "B2vpnId=\"vpn%i\"", $i++/e;
              $i = $start if eof' test 

またはデフォルトの1に設定してからもし最初の引数は既存のファイルではありません。最初の引数に設定してください。

$ perl -p -e 'BEGIN {
                $start = 1;
                $start = shift unless -f $ARGV[0];
                $i = $start
              };

              s/B2vpnId=""/sprintf "B2vpnId=\"vpn%i\"", $i++/e;

              $i = $start if eof' 10 test* 

答え2

これは次の場合に適していますawk

$ awk 'BEGIN { c=1 } /B2vpnId/ { sub(/vpnId="/, "vpnId=\"vpn"c,$0); c++ }1' test
        <Set Id="16">
                <Ver="44" Sniff="no" B2vpnId="vpn1" Collision="no">
        </Set>        
        <Set Id="17">
                <Ver="22" Sniff="no" B2vpnId="vpn2" Collision="no">
        </Set>

実際にファイルを変更するには(GNU awk):

$ awk -i inplace <rest of command>

注:参照猫に役に立たない用途;

答え3

最も簡単な方法は、出力を一時ファイルにリダイレクトし、一時ファイルを元のファイルの上に移動することです。

しかし、コマンドラインの複雑さに少し驚きました。

i=1
while read -r line; do
    echo "$line"|sed -e 's/B2vpnId=""/B2vpnId="'"vpn$i"'"/'
    ((i+=1))
done <<< "$(cat test|grep B2vpnId=)"

より簡単な方法は次のとおりです。

i=1
grep 'B2vpnId=' test |
while read -r line; do 
    echo "$line"|sed -e 's/B2vpnId=""/B2vpnId="'"vpn$i"'"/'
    ((i+=1))
done

ただし、変更されていない行をエコーする必要がある場合は、grepループに入れる必要があります。

i=1
while read -r line; do 
    if echo "$line" | grep -q 'B2vpnId=' ; then
        echo "$line"|sed -e 's/B2vpnId=""/B2vpnId="'"vpn$i"'"/'
        ((i+=1))
    else
        echo "$line"
    fi
done < test

これは独自のソリューションに基づいています。

関連情報