텍스트 파일의 grep 번호 범위

텍스트 파일의 grep 번호 범위

텍스트 파일에 다음 텍스트가 있습니다

$ cat test
20180618:
20180619:
20180620:
20180621:
20180622:
20180623:
20180624:

grep을 사용하여 다음과 같이 숫자의 범위를 찾으려고 합니다.

$ grep 201806{19..21} test
grep: 20180619: No such file or directory
grep: 20180620: No such file or directory
grep: 20180621: No such file or directory

ZSH와 bash 모두에서 위의 오류가 발생합니다. grep이 문자열을 파일로 검색하는 것처럼 보입니다.

나는 다른 방법을 시도했습니다.

$ grep 201806* test       
zsh: no matches found: 201806*

ZSH에서만 이 오류가 발생합니다. ZSH에서 이것을 사용하는 올바른 방법은 무엇이며 *, 숫자 범위에 대해 grep에게 grep을 지시하려면 어떻게 해야 합니까?

답변1

grep그냥 치료해첫 번째기본적으로 매개변수는 정규식으로 사용됩니다.

이것은 의미한다

grep {1..9} file

다음으로 확장

grep 1 2 3 4 5 6 7 8 9 file

grepwith는 1다른 피연산자와 일치하는 표현식으로 호출되며 이러한 다른 피연산자는 파일 이름이 될 것으로 예상됩니다.

다른 명령:

grep 201806* test

이는 201806*파일 이름 와일드카드 패턴으로 일치를 시도합니다. 201806현재 디렉토리에 이름이 다음으로 시작하는 파일이 없으므로 zsh쉘은 패턴을 확장할 수 없으며 오류 메시지를 표시합니다 no matches found.

Bourne과 같은 다른 쉘에서는 패턴이 파일 이름과 일치하지 않으면 확장되지 않은 상태로 유지되고 로 사용됩니다 grep. 표현식이 201806*정규 표현식으로 처리되면 일치 항목 20180뒤에 0개 이상의 6문자가 옵니다(예: ) 2018066666.

대신, 범위와 일치하는 정규식을 구성할 수 있습니다.

grep -E '201806(19|20|21)' test

또는

grep -E '201806(19|2[01])' test

표현식의 (교대)를 이해해야 합니다 -E(이 교대로 하면 확장 정규 표현식이 됩니다) grep.|


중괄호 확장을 통해 정규식을 구성할 수도 있습니다.

set -- {19..21}
re=$( IFS='|'; printf '201806(%s)' "$*" )

grep -E "$re" test

그러면 먼저 위치 매개변수 및 가 범위 내에서 원하는 숫자 $1로 설정됩니다. 그러면 변수는 로 구분된 숫자로 대체될 위치로 설정됩니다 .$2$3re201806(%s)printf%s|

이 호출은 정규식 grep으로 사용됩니다 .201806(19|20|21)

답변2

grep 201806{19..21} test

셸을 통해 다음으로 확장되었습니다.

grep 20180619 20180620 20180621 test

이는 3개의 파일을 grep찾는 것으로 이해될 수 있습니다.201806192018062020180621test

다음과 같이 변경하면:

grep -e201806{19..21} test

그런 다음 다음으로 확장합니다.

grep -e20180619 -e20180620 -e20180621 test

에서 검색할 수 있는 3가지 표현이 제공됩니다 e.greptest

아니면 이렇게 할 수도 있습니다:

printf '%s\n' 201806{19..21} | grep -f - test

표현식을 여러 줄 입력으로 전달합니다 (일부 구현의 경우 this 로 대체 grep해야 할 수도 있음 )./dev/stdin-

구체적으로 zsh다음을 수행할 수도 있습니다.

numbers=({19..21} 25 31)
grep -E "201801(${(j:|:)numbers})" test

ERE로 사용할 수 있도록 (j:|:)매개변수 확장 플래그를 사용하여 배열 요소를 (확장 정규식 대체 연산자)와 연결합니다.|

또는 다음을 사용하여 배열을 정규식 스칼라에 바인딩할 수 있습니다.

$ typeset -T re numbers '|'
$ numbers=({19..21} 25 31)
$ echo $re
19|20|21|25|31

정규식에는 일반적으로 숫자 범위 일치 기능이 없지만 zsh패턴( extendedglob기능적으로 정규식과 동일)은 <x-y>연산자를 사용할 수 있습니다(십진수 시퀀스에만 해당).

print -rl -- ${(M)${(f)"$(<test)"}:#*201806<19-21>*}

답변3

따옴표가 없는 문자열은 명령을 실행하기 전에 쉘에 의해 해석됩니다. 귀하의 경우 시도 중인 명령은 다음으로 확장됩니다.grep 20180619 20180620 20180621 test

$ echo grep 201806{19..21} test
grep 20180619 20180620 20180621 test

한 가지 해결 방법은 정규식 대체를 지정하는 것입니다.

$ grep -E '201806(19|20|21)' test
20180619:
20180620:
20180621:

정규식을 사용하여 숫자 범위를 구성할 수 있지만 쉽지 않습니다. 바라보다https://www.regular-expressions.info/numericranges.html자세한 내용은


또 다른 옵션은 다음을 사용하는 것입니다.awk

$ awk -F: '$1>=20180619 && $1<=20180621' ip.txt
20180619:
20180620:
20180621:

여기서는 선을 분할 :한 다음 첫 번째 필드를 $1원하는 범위와 비교합니다.

답변4

  1. POSIX쉘(아니요 bash) 및 유틸리티:

    seq 20180618 20180624 | grep -f - test
    
  2. numgrep:

    numgrep '/20180618..20180624/' < test
    

관련 정보