次のファイルがあります。
:~$ 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
その実行フェーズでのみ実行されます。詳細は参照してください。INIT
CHECK
UNITCHECK
perlmod
$i
増えることを覚えておいてください今後毎回使用される(スクリプトがusing++$i
の代わりに使用され、$i++
毎回使用されるたびに値が増加するため)、開始数字から1を減算します。
また、たとえばBEGINブロックを使用している場合は、$i=0 if eof
BEGINブロックと一致するように変更する必要があります。$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
これは独自のソリューションに基づいています。