gnu sed는 어떻게 bash 기능을 실행하여 카운터 값을 증가합니까?
$ k(){ ((i++)); echo $i ;} ;export -f k
$ i=; echo -e 'oAo\nooAo\nAo' | sed -E '/A/e k'
1
oAo
1
ooAo
1
Ao
카운터를 올바르게 수행하는 대신 실패합니다.
답변1
프로세스는 상위 프로세스의 환경에 영향을 미칠 수 없습니다. sed의 명령을 사용하면 e
새 셸을 포크하여 명령을 실행하고(내보낸 k
함수를 상속함) 해당 셸 변수에 대한 모든 변경 사항은 i
종료 시 손실됩니다(즉, k 함수가 hour를 종료함).
변수를 증가시키려면 환경 외부 어딘가에 변수를 저장해야 합니다. 예를 들어 파일에서. 파일을 사용하고 싶지 않다면 SQL 데이터베이스 같은 것을 사용하지 못할 이유가 memcached
없습니다 redis
.
예를 들어:
k() {
local counterfile i
counterfile='/tmp/counter.i'
[ -e "$counterfile" ] && i=$(cat "$counterfile")
((i++))
echo "$i" | tee "$counterfile"
}
export -f k
지금 실행하면 카운터가 증가합니다.
$ rm /tmp/counter.i
$ printf 'oAo\nooAo\nAo' | sed -E '/A/e k'
1
oAo
2
ooAo
3
Ao
$ echo 100 > /tmp/counter.i
$ printf 'oAo\nooAo\nAo' | sed -E '/A/e k'
101
oAo
102
ooAo
103
Ao
그런데, sed의 e
명령이 유용하기는 하지만, 내 생각에는 Perl이 여러분이 하고 있는 작업에 더 나은 언어라고 생각합니다. 예를 들어
$ printf 'oAo\nooAo\nAo' |
perl -lpe 'BEGIN { $i=shift || 0 };
if (/A/) {print ++$i}' 10
11
oAo
12
ooAo
13
Ao
인수를 제공하지 않으면 $i
기본값은 0입니다.
스크립트가 "sed"처럼 보이도록 하려면 $i
포함된 줄에 카운터 변수를 증가시키고 인쇄하는 방법이 있습니다 A
. 여기 또 다른 것이 있습니다:
/A/ && print ++$i
한 줄에 여러 일치 항목이 있을 수 있고 각 일치 항목에 대해 카운터를 증가시키고 인쇄해야 하는 경우 일치 항목을 반복해야 합니다. 예를 들어
$ printf 'oAo\noAoAoAo\nAo' |
perl -lpe 'BEGIN { $i=shift || 0 };
for (/oA/g) {print ++$i}'
1
oAo
2
3
4
oAoAoAo
Ao
또는 누적 합계가 필요하지 않지만 각 행의 일치 항목 수를 계산하는 경우:
$ printf 'oAo\noAoAoAo\nAo' |
perl -lne '$c = () = $_ =~ /oA/g;
printf "%02i:%s\n", $c, $_'
01:oAo
03:oAoAoAo
00:Ao
이 마지막 항목에는 설명이 필요할 수 있습니다. 읽어뒤로, 이 $c = () = $_ =~ /oA/g
문은 먼저 정규식 일치를 수행 /oA/g
하고 목록 컨텍스트의 결과를 빈 목록으로 반환한 ()
다음 변수에 할당합니다 $c
. 배열/목록이 아닌 스칼라 변수이기 때문에 $c
스칼라 컨텍스트에서 평가되므로 해당 목록의 요소 수를 반환합니다. 이것은 일치 항목 수를 계산하는 데 사용되는 매우 일반적인 Perl 관용어입니다.
참고: Perl에는 -p
sed와 매우 유사하게 실행되도록 하는 옵션이 있습니다(즉, 입력을 반복하고 명령문이 인쇄를 방해하지 않는 한 처리 후 각 줄을 인쇄합니다). Perl에는 -n
다음과 같이 동작하도록 하는 옵션이 있습니다 sed -n
(입력을 반복하고 명시적으로 인쇄하라는 지시만 인쇄합니다).
마지막으로, 이 버전의 sed 및 bash 함수는 일치하는 각 입력 행에 대해 bash
, cat
및 를 분기한다는 점에 주목할 가치가 있습니다 . Perl 버전은 아무것도 포크하지 않고 입력만 반복합니다. bash에서 수행하기 위해 외부 프로그램을 포크해야 하는 많은 작업은 자체 내장 구문(또는 수천 개의 라이브러리 모듈 중 하나)을 사용하여 Perl을 통해 내부적으로 수행될 수 있다는 점도 주목할 가치가 있습니다.tee
/A/
답변2
GNU sed를 사용하면 원하는 결과를 얻기 위해 사용자 정의 함수에 의존할 필요가 없습니다. 여기서는 증분 개수를 저장하기 위해 저장 공간을 사용합니다.
echo -e 'oAo\nooAoAoA\no' |
sed -En "/A/!d
p;:a
H;g
s/^(\n*).*/expr '\1' : '.*'/ep
g;s//\1/;x;s/^\n*//;s/A//;//ba
"
산출:
oAo
1
ooAoAoA
2
3
4