다음 파일이 있습니다.
:~$ 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
테스트된 두 번째 복사본의 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를 사후 증가시킬 수 있습니다(그리고 다른 변수를 시작 값으로 사용하므로 한 곳에서만 변경하면 됩니다).
$ 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
이는 자체 솔루션을 기반으로 합니다.