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
awk
Bash와 함께 사용할 수 있습니다 read
.
read -r FIRST_NAME LAST_NAME <<< $(awk -F, -v cid="$customer_id" '$0~cid{print $5,$6}' customer-info)
-F
필드 구분 기호로 쉼표를 사용하도록 awk에 지시
-v
awk 변수를 cid
쉘 변수로 설정$customer_id
행이 일치하면 $customer_id
awk는 5번째와 6번째 필드를 인쇄하고 이 필드에는 변수 FIRST_NAME
sum 이 할당됩니다 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
. 이제 head
0.01초(grep의 약간의 오버헤드 포함) 내에 출력하는 한 안정적으로 작동합니다. 이는 최신 시스템에서 공정한 베팅이고 사용자가 알아차리지 못할 정도로 짧습니다.
일부 명령의 헤더와 출력을 모두 사용하는 것을 만들고 싶기 때문에 다음과 같이 일반화할 수 있습니다.
function grabheader {
tee >(head -1 >&2)
}
함수의 명령 은 tee
stdin에서만 읽고 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% /
그러나 이제 우리는 어떤 명령으로도 이를 수행할 수 있는 일반적인 방법을 갖게 되었습니다.