어떻게 두 번 grep할 수 있나요?

어떻게 두 번 grep할 수 있나요?

grep파일에서 두 번 수행하지 않고 변수를 한 번만 채우는 방법이 있습니까 ? 파일이 작아서 별 문제는 아니지만 한 번에 완료할 수 있는지 알고 싶었습니다.

FIRST_NAME=$(grep "$customer_id" customer-info|cut -f5 -d,)
LAST_NAME=$(grep "$customer_id" customer-info|cut -f6 -d,)

답변1

쉘 문자열 대체 grep을 한 번 사용하고 두 번 분할할 수 있습니다.

NAME=$(grep "$customer_id" customer-info | cut -f5,6 -d,)
FIRST_NAME=${NAME%,*}
LAST_NAME=${NAME#*,}

또는 bash의 경우 프로세스 대체를 사용합니다.

IFS=, read FIRST_NAME LAST_NAME < <(grep "$customer_id" customer-info | cut -f5,6 -d,)

read입력을 분할 IFS하고 첫 번째 값을 에 할당 FIRST_NAME하고 나머지 값을 에 할당합니다 LAST_NAME. 프로세스 대체 및 리디렉션을 사용하면 하위 쉘을 사용하지 않고도 의 출력을 전달할 수 있습니다 < <(...).grep ... | cut ...read

답변2

가장 쉬운 방법은 전체 레코드를 변수에 넣어서 사용하는 cut것입니다.

RECORD=$(grep "$customer_id" customer-info)
FIRST_NAME=$(echo "$RECORD"|cut -f5 -d,)
LAST_NAME=$(echo "$RECORD"|cut -f6 -d,)

또한 개인적으로 좀 더 구체적인 정규식을 사용하는 것이 좋습니다. 고객 ID가 항상 줄의 시작 부분에 있는 경우 일치 항목이 줄의 시작 부분에 있도록 요구하는 grep '^'"$customer_id"대신 쓸 수 있습니다 . grep "$customer_id"그렇지 않으면 고객 ID와 일치하는 텍스트가 레코드의 다른 곳에 나타나는 레코드를 선택할 수 있습니다.

답변3

awkBash와 함께 사용할 수 있습니다 read.

read -r FIRST_NAME LAST_NAME <<< $(awk -F, -v cid="$customer_id" '$0~cid{print $5,$6}' customer-info)

-F필드 구분 기호로 쉼표를 사용하도록 awk에 지시

-vawk 변수를 cid쉘 변수로 설정$customer_id

행이 일치하면 $customer_idawk는 5번째와 6번째 필드를 인쇄하고 이 필드에는 변수 FIRST_NAMEsum 이 할당됩니다 LAST_NAME.

이름($5)에 공백(예: a,b,c,d,Sarah Jane,Smith)이 포함된 경우 필드 사이에 -v OFS=,출력 쉼표를 추가 awk하고 접두사 read로 with 를 추가 IFS=,하여 쉼표에서 분할되도록 합니다.

또한 -- awk와 같은 특정 필드 내에서만 검색이 가능하며 '$3~cid{print..}'해당 필드와 일치합니다.모두'$3~"^"cid"$"{print...}'귀하의 ID에 중요한 경우 필드입니다.

답변4

기존 답변은 모두 출력을 메모리(변수)에 저장하고 두 번 재생합니다. 임의로 큰 입력을 수용하고 이에 대해 두 가지 작업을 수행할 수 있는 일반 래퍼를 만들려는 경우 이는 문제가 됩니다. 대신 출력 스트림을 복사하여 두 명령 모두에 스트리밍할 수 있습니다.

내 경우 목적은 출력 스트림에서 임의로 길 수 있는 헤더(첫 번째 줄)와 특정 줄 집합을 필터링하는 것입니다. 간단한 예는 디스크 공간 사용량을 표시하는 것입니다.

$ df -h | tee >(head -1 >&2) | grep '/$'
Filesystem    Size  Used Avail Use% Mounted on
/dev/sda1     202G  145G   57G  72% /

df -h사용하려는 명령으로 바꾸고 적용하려는 두 명령으로 head -1바꾸십시오 . grep '/$'두 가지 모두의 출력이 터미널에 표시되지만 전자 명령의 출력은 후자 뒤에 나타날 수 있습니다.

어떻게 작동하나요?

  • 프로그램은 tee"각 [인수]에 표준 입력을 복사하고 표준 출력에도 복사합니다." 따라서 를 사용하여 stdin의 출력을 stdout 및 stderr로 보낼 수 있습니다 command | tee /dev/stderr.
  • command >(command2)구문은 bash의 인수로 대체되므로 command /dev/fd/63실행됩니다. command을 쓰려고 하면 /dev/fd/63의 입력(stdin)으로 종료됩니다 command2. 이를 프로세스 대체라고 합니다(참고자료 참조 man bash).
  • tee인수(명령 대체를 인수로 전달)와 표준 출력이 동시에 작성되므로 다른 파이프를 추가하고 다른 명령을 실행할 수 있습니다 . 이제 우리는 그것을 갖게 되었습니다 command | tee >(command2) | command3.
  • 마지막으로, command2는 stdout으로 출력하고 stdout은 로 파이프되므로 command3(내 예에서는) 헤더 라인을 grep합니다. 그것은 우리가 원하는 것이 아닙니다. 우리는 그것을 보여주고 싶습니다. 우리는 stderr를 파이핑하지 않기 때문에 출력을 stderr로 리디렉션하는 것이 터미널에 표시하는 쉬운 방법입니다. 즉, 를 추가하면 >&2결과는 입니다 command | tee >(command2 >&2) | command3.

문제가 있습니다. 출력 순서는 임의일 수 있습니다. 우주선에 따라 위 또는 다음을 볼 수 있습니다.

$ df -h | tee >(head -1 >&2) | grep '/$'
/dev/sda1     202G  145G   57G  72% /
Filesystem    Size  Used Avail Use% Mounted on

이 문제를 해결하는 구식이지만 신뢰할 수 있는 방법은 (과도하게 설계되거나 구식이 아닌 방법이 아닌) 두 번째 명령에 다음과 같은 짧은 절전 모드를 추가하는 것입니다.

$ df -h | tee >(head -1 >&2) | sleep 1; grep '/$'

grep하지만 잠깐만요. 이제 출력이 tee에서 로 파이프되어 sleep입력 grep을 무기한 기다리기 때문에 두 번째 명령( )이 중단됩니다 . 이 문제를 해결하기 위해 서브셸을 추가합니다.

$ df -h | tee >(head -1 >&2) | (sleep 0.01; grep '/$')
Filesystem    Size  Used Avail Use% Mounted on
/dev/sda1     202G  145G   57G  72% /

이제 출력은 서브셸이 아닌 grep서브셸로 리디렉션됩니다. sleep여기서 읽지 않으므로(스트림을 소비하지 않음) 계속해서 읽을 수 있습니다 grep. 이제 head0.01초(grep의 약간의 오버헤드 포함) 내에 출력하는 한 안정적으로 작동합니다. 이는 최신 시스템에서 공정한 베팅이고 사용자가 알아차리지 못할 정도로 짧습니다.

일부 명령의 헤더와 출력을 모두 사용하는 것을 만들고 싶기 때문에 다음과 같이 일반화할 수 있습니다.

function grabheader {
    tee >(head -1 >&2)
}

함수의 명령 은 teestdin에서만 읽고 stdout으로 출력하므로 df -h | grabheader | grep '/$'.

function grabheader {
    tee >(head -1 >&2) | (sleep 0.01; cat)
}

cat이는 표준 입력으로 전달된 모든 항목이 다시 표준 출력으로 이동하도록 보장합니다. 매개변수를 전달하지 않고 리디렉션을 추가하지 않음으로써 이를 수행합니다. 용법:

$ df -h | grabheader | grep '/$'
Filesystem    Size  Used Avail Use% Mounted on
/dev/sda1     202G  145G   57G  72% /

물론 특별한 경우에는 df이 작업을 더 간단하게 수행할 수 있습니다.

$ df -h /
Filesystem    Size  Used Avail Use% Mounted on
/dev/sda1     202G  145G   57G  72% /

그러나 이제 우리는 어떤 명령으로도 이를 수행할 수 있는 일반적인 방법을 갖게 되었습니다.

관련 정보