다음 grep/awk 쿼리를 완료하는 더 나은 방법을 찾으려고 노력 중입니다. 다음은 문제의 간단한 예입니다.
나는 정규식을 사용하여 이것을 달성했습니다.
grep -Po ^(?:[^,]+,\s?){7}(Want|Need) | awk -F ',' 'NR>=2{print $8}' | sort | uniq -c
내 CSV 파일은 다음과 같습니다.
1896,Ranger,2021,State,Postcode,Surname,Industry,Want,Turbo,Good
1896,Ranger,2021,State,Postcode,Surname,Industry,Selling,Turbo,Good
1896,Ranger,2021,State,Postcode,Surname,Industry,Need,Turbo,Good
위 작업은 grep을 사용하여 전체 줄을 인쇄합니다.
1896,Ranger,2021,State,Postcode,Surname,Industry,Want
1896,Ranger,2021,State,Postcode,Surname,Industry,Need
그런 다음 열 8의 값을 계산할 수 있습니다. 내 질문은 정규식을 사용하여 선택한 그룹만 반환하도록 grep/regex 쿼리를 작성하는 방법입니다.
예를 들어:
Want
Need
이 글을 쓴 이유는 순전히 여기에서 정규식을 사용하는 더 나은 방법을 이해하기 위해서입니다. 나는 이것을 수행하는 다른 방법이 있다는 것을 알고 있습니다.
답변1
PCRE 어설션을 찾고 있는 것 같습니다 \K
. ~에서페레:
\K(Perl 5.10.0부터 사용 가능)라는 특별한 형태의 이 구성이 있는데, 이는 정규식 엔진이 $&로 묶지 않고 \K 이전에 일치하는 항목을 "유지"하도록 합니다.
그래서
$ grep -Po '^(?:[^,]+,\s?){7}\K(Want|Need)' file.csv
Want
Need
보다 일반적으로 이런 종류의 작업은 다음을 사용하여 수행됩니다.뒤를 봐주장 - 그러나 Perl은 가변 길이 뒤돌아보기를 지원하지 않으며 grep -P도 지원하지 않습니다.
$ grep -Po '^(?<=(?:[^,]+,\s?){7})(Want|Need)' file.csv
grep: lookbehind assertion is not fixed length
당신은 또한 볼 수 있습니다앞으로 및 뒤로 길이가 0인 어설션
답변2
이것은 일치를 수행하기 위해 libpcre(perl 정규 표현식의 독립 실행형 구현)를 사용하는 -P
GNU 구현의 비표준(선택적이며 오랫동안 실험적으로 간주된) 옵션입니다.grep
libpcre
이제 완전한 구현으로 발전하여 일부 GNU/Linux 배포판의 자체 패키지에서 찾을 수 있지만 grep
예제 코드( )로 자체 명령이 제공됩니다 .pcregrep
grep
pcregrep
grep
해당 캡처 그룹을 출력하기 위해 선택적 숫자 인수를 사용하도록 GNU 의 -o
비표준 옵션을 확장했습니다 .
그래서 여기 있습니다:
pcregrep -o1 '^(?:[^,]+,\s?){7}(Want|Need)'
또는 GNU가 없는 시스템 grep
(또는 grep
PCRE 지원 없이 GNU를 구축한 시스템) 에서도 작동할 수 있다는 장점이 있는 실제 시스템을 사용할 수도 있습니다 pcregrep
.
perl -lne 'print $1 if /^(?:[^,]+,\s?){7}(Want|Need)/'
그러나 perl
기본적으로 입력은 GNU에서와 같이 로케일의 텍스트 인코딩에 따라 디코딩되지 않습니다 grep
. 이 특별한 경우, 일치하는 텍스트는 이식 가능한 문자 집합의 문자만 사용합니다. 이는 입력이 로케일과 다른 인코딩으로 되어 있어도 여전히 작동하므로 매우 유리할 수 있습니다.
perl
입력의 텍스트를 로캘 인코딩에 따라 디코딩(및 출력에서 인코딩) 하려면 를 추가하면 됩니다 -Mopen=locale
.
그러나 귀하의 경우에는 Perl 정규 표현식을 사용할 가치가 없습니다. 여기에서 사용하는 모든 Perl 연산자에는 표준 ERE 연산자와 동등한 것이 있습니다(대체를 제외한 BRE도 마찬가지).
(?:...)
: 단지 perl/ERE(...)
또는 BRE 이며\(...\)
캡처가 없습니다.+
: ERE에서도 동일,\{1,\}
BRE에서도 동일?
: ERE와 동일,\{0,1\}
ERE에서는{7}
: ERE에서도 동일,\{7\}
BRE에서도 동일(Want|Need)
: ERE와 동일합니다(교대 방향을 선택할 때 동작이 약간 다르지만).\s
:[[:space:]]
BRE와 ERE에서^
,[^,]
: BRE나 ERE에서 동일
sed
는 패턴의 일치하는 부분을 추출하는 도구입니다(반면 , grep
after ed
명령은 정규식과 일치하는 행을 g/re/p
인쇄합니다 ). p
표준은 BRE를 사용하지만 대부분의 구현에서는 ERE로의 전환을 지원합니다(이는 표준의 다음 버전에 추가될 예정입니다).r
e
sed
sed
-E
따라서 여기에서는 perl
위 명령과 동일하게 이식 가능한 작업도 수행할 수 있습니다.
LC_ALL=C sed -nE 's/^([^,]+,[[:space:]]?){7}(Want|Need).*$/\2/p'
아니면 -E
:
LC_ALL=C sed -n 's/^\([^,]\{1,\},[[:space:]]\{0,1\}\)\{7\}\(Want\).*$/\2/p; t
s/^\([^,]\{1,\},[[:space:]]\{0,1\}\)\{7\}\(Need\).*$/\2/p'
아니면 다른 것으로 바꾸세요 Want
:Need
LC_ALL=C sed -E 's/^(([^,]+,[[:space:]]?){7})(Want|Need)/\1Desire/'
LC_ALL=C sed 's/^\(\([^,]\{1,\},[[:space:]]\{0,1\}\)\{7\}\)Want/\1Desire/; t
s/^\(\([^,]\{1,\},[[:space:]]\{0,1\}\)\{7\}\)Need/\1Desire/
1 그 이후로 다른 구현에서는 ast-open처럼 항상 libpcre를 사용하는 것이 아니라 유사한 정규식을 -P
사용하기 위한 자체 옵션을 추가했습니다 (탐색 주장은 지원하지만 지원하지 않음 ).perl
grep
\K
답변3
이미 awk를 사용하고 있으므로 여기서는 필요하지 않습니다 grep
. 당신은 그것을 필요로하지도 않고 sort
필요 uniq -c
하지도 않습니다. 예를 들어:
$ awk -v search=Want -F, '$8 ~ search { count[$8]++ };
END { for (f in count) { printf "%5i\t%s\n", count[f], f}}' input.csv
1 Want
$ awk -v search='Want|Need' -F, '$8 ~ search { count[$8]++ };
END { for (f in count) { printf "%5i\t%s\n", count[f], f}}' input.csv
1 Want
1 Need
또는 일치하는 줄도 인쇄하려면 다음을 수행하십시오.
$ awk -v search='Want|Need' -F, '$8 ~ search { count[$8]++ ; print };
END { for (f in count) { printf "%5i\t%s\n", count[f], f}}' input.csv
1896,Ranger,2021,State,Postcode,Surname,Industry,Want,Turbo,Good
1896,Ranger,2021,State,Postcode,Surname,Industry,Need,Turbo,Good
1 Want
1 Need
-v IGNORECASE=1
명령줄에 추가하여 GNU awk에 대소문자 구분을 추가하거나 원하는 경우 정확한 일치와 같은 고급 기능을 추가할 수도 있습니다.
$ awk -v search='want' -v exact=1 -v IGNORECASE=1 -F, '
BEGIN {if (exact == 1) search = "^(" search ")$"};
$8 ~ search { count[$8]++ ; print };
END { for (f in count) { printf "%5i\t%s\n", count[f], f}}' input.csv
1896,Ranger,2021,State,Postcode,Surname,Industry,Want,Turbo,Good
1 Want
다음은 ant
에 있는 동안 Want
전체 필드 8과 정확히 일치하지 않기 때문에 출력을 생성하지 않습니다.
$ awk -v search='ant' -v exact=1 -v IGNORECASE=1 -F, '
BEGIN {if (exact == 1) search = "^(" search ")$"};
$8 ~ search { count[$8]++ ; print };
END { for (f in count) { printf "%5i\t%s\n", count[f], f}}' input.csv
참고: 명령줄 옵션 처리를 수행하는 더 좋은 방법이 분명히 있습니다(예:선택 항목 가져오기함수 또는 쉘 스크립트 래퍼를 작성하여 sh/bash 내장 기능을 사용 getopt
하지만 -v
awk의 옵션을 사용하여 스크립트 외부에서 awk에 변수를 설정하는 것은 이와 같은 간단한 작업에 간단하고 편리합니다.
그런데 awk는 변수를 스크립트 자체 뒤의 명령줄에 추가하여 사용하지 않고 변수를 할당할 수도 있습니다 -v
(awk는 형식의 모든 인수를 x=y
변수 x를 값 y로 설정하는 것으로 해석합니다. 불행하게도 이로 인해 사용하기가 어렵습니다. 그들 안에 =
– 아마도 불가능할 수도 있습니다. "그럼 하지 마세요" 이외의 해결책을 찾았는지 기억이 나지 않습니다).
그러나 를 사용할 때와는 달리 -v
이러한 변수는아니요이는 BEGIN {}
성명서에서 확인할 수 있다. 예를 들어, ant
다음을 설정하더라도 다음은 일치합니다 exact=1
.
$ awk -F, 'BEGIN {if (exact == 1) search = "^(" search ")$"};
$8 ~ search { count[$8]++ ; print };
END { for (f in count) { printf "%5i\t%s\n", count[f], f}}' \
search=ant IGNORECASE=1 exact=1 input.csv
1896,Ranger,2021,State,Postcode,Surname,Industry,Want,Turbo,Good
1 Want
GNU awk 매뉴얼 페이지에서:
명령줄의 파일 이름이 다음 형식인 경우
var=val
변수 할당으로 처리됩니다. 변수에var
값이 할당됩니다val
. (가끔 이런 경우가 있어요.뒤쪽에BEGIN
실행된 규칙이 없습니다. )명령줄 변수 할당은 AWK가 입력이 필드와 레코드로 분류되는 방식을 제어하는 데 사용하는 변수에 값을 동적으로 할당하는 데 가장 유용합니다. 단일 데이터 파일에 여러 번 전달해야 하는 경우 상태를 제어하는 데에도 유용합니다.
IMO에서는 이를 이전 awk 스크립트와 호환되는 레거시 기능으로 처리하고 -v
.
-v var=val --assign var=val
프로그램이 실행되기 전에 변수에 값이 할당됩니다
val
.var
이러한 변수 값예BEGIN
AWK 프로그램 에 사용 가능한 규칙입니다.
(위 인용문 안의 "이후"와 "이다"는 제가 굵은 글씨로 강조한 것입니다)
답변4
0
검색 중인 문자열 중 하나가 입력에 나타나지 않는 경우 전혀 인쇄하지 않고 개수를 인쇄하는 것을 보고 싶다면 강력하고 휴대 가능하며 효율적이고 간결한 방법은 다음과 같습니다. 이것:
$ awk -F',' -v tgts='Want,Need' '
{ cnt[$8]++ }
END { split(tgts,t); for (i in t) print t[i], cnt[t[i]]+0 }
' file
Want 1
Need 1
따라서 여기서 정규식이 어디에 적용되는지 파악하기가 어렵습니다. 어쩌면 다음과 같을 수도 있습니다:
$ awk -F',' -v tgts='Want|Need' '
$8 ~ ("^"tgts"$") { cnt[$8]++ }
END { split(tgts,t,/[|]/); for (i in t) print t[i], cnt[t[i]]+0 }
' file
Want 1
Need 1
또는:
$ awk -F',' -v tgts='Want|Need' '
$0 ~ ("([^,]*,){7}"tgts"(,|$)") { cnt[$8]++ }
END { split(tgts,t,/[|]/); for (i in t) print t[i], cnt[t[i]]+0 }
' file
Want 1
Need 1
그러나 정규식은 스크립트를 복잡하게 만들고 더 취약하게 만들 뿐이며(찾으려는 문자열에 .
또는 같은 정규식 메타 문자가 포함되어 있으면 정규식이 있는 스크립트는 실패 *
하지만 첫 번째 스크립트는 계속 작동합니다), 다음이 없으면 아무 값도 추가하지 않습니다. $8
귀하의 입력에는 수십억 개의 고유 값이 있습니다.