bash는 문자열로 시작하는 줄을 찾습니다.


10

나는 많은 파일을 가지고 있으며 어떤 문자열이 특정 문자열로 시작하는 순차 줄을 포함하고 있는지 찾고 싶습니다.

예를 들어 다음 파일의 경우 :

Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Cyyyyyyyyy
Czzzzzzzzz
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd
Ceeeeee

'C'로 시작하는 줄이 두 개 이상 있으므로이 파일을 명령으로 찾을 수 있습니다.
예를 들어 다음 파일의 경우 :

Aaaaaaaaaaaa
Baaaaaaaaaaa
Cxxxxxxxxx
Abbbbbbbbbbb
Bbbbbbbbbbbb
Caaaaaa
Accccccccccc
Bccccccccccc
Cdddddd

항상 'C'로 시작하는 한 줄이 있습니다.이 파일을 원하지 않습니다. 나는 a grep또는 a 를 사용하려고 생각 sed했지만 어떻게 해야하는지 정확하게 모르겠습니다. 아마 정규 표현식 ^C.*$^C이나 그와 비슷한 것을 사용할 수 있습니다. 어떤 아이디어?


C번째 예제 에는 두 줄이 있습니다 .
cuonglm

5
이 질문은 명확하지 않습니다. ?로 시작하는 연속 된 줄 이 두 개 이상인 파일을 찾고 C있습니까?
Graeme

예, 이것이 내가 원하는 것입니다. 오해해서 죄송합니다.
Jérémie

2
@terdon, -P를 사용한 여러 줄 검색은 2.5.4까지 작동했지만 그 이후로는 더 이상 작동하지 않지만 변경 로그에서 이유를 설명하는 내용을 찾을 수는 없습니다.
Stéphane Chazelas

1
@Graeme 당신은 당신의 답변을 삭제 취소하고 싶을 수도 있습니다, Stephane의 의견을보십시오, 분명히 일부 구형 grep버전 에서는 작동합니다 .
terdon

답변:


5

pcregrep:

pcregrep -rMl '^C.*\nC' .

POSIXly :

find . -type f -exec awk '
  FNR==1 {last=0; printed=0; next}
  printed {next}
  /^C/ {if (last) {print FILENAME; printed=1; nextfile} else last=1; next}
  {last=0}' {} +

(이것은 awk지원하지 않는 구현으로 모든 파일을 완전히 읽는 것을 의미하지만 nextfile).


GNU 버전이 grep2.5.4 이하인 경우 :

grep -rlP '^C.*\nC' .

작동하는 것처럼 보이지만 실수로 작동하는 것은 아닙니다.

2.6에서 수정되기 전에 ( 이 커밋으로 ) GNU grep는 현재 사용하고있는 pcre 검색 기능이 현재 처리 한 전체 버퍼에서 일치 grep하여 모든 종류의 놀라운 동작이 발생 한다는 것을 간과했습니다 . 예를 들어 :

grep -P 'a\s*b'

다음을 포함하는 파일에서 일치합니다.

bla
bla

이것은 다음과 일치합니다.

printf '1\n2\n' | grep -P '1\n2'

하지만 이것은:

(printf '1\n'; sleep 1; printf '2\n') | grep -P '1\n2'

또는:

(yes | head -c 32766; printf '1\n2\n') > file; grep -P '1\n2' file

는에 1\n2\n의해 처리되는 두 개의 버퍼에 있기 때문에 그렇지 않습니다 grep.

그 행동은 결국 문서화되었습니다.

15- 라인간에 어떻게 일치시킬 수 있습니까?

표준 grep은 기본적으로 라인 기반이므로이 작업을 수행 할 수 없습니다. 따라서 '[: space :]'문자 클래스 만 사용하면 예상 한대로 줄 바꿈이 일치하지 않습니다. 그러나 Grep이 Perl 패턴을 사용하여 컴파일 된 경우 Perl 's'수정 자 ( '.'를 개행과 일치하게 함)를 사용할 수 있습니다.

     printf 'foo\nbar\n' | grep -P '(?s)foo.*?bar'

가 2.6에서 수정 된 후, 문서가 개정되지 않았다 (I는 한 번보고 있다 ).


사용되지 않은 이유가 exit-exec \;대신 nextfile의는?
terdon

@terdon, 그것은 awk파일 당 하나를 실행하는 것을 의미 합니다. awk지원하지 않고 nextfile파일의 크기가 크며 파일의 시작 부분에 일치하는 줄이있는 경우에만 그렇게하고 싶습니다 .
Stéphane Chazelas

줄 종결자를 NUL로 설정하여 전체 파일을 단일 문자열처럼 보이게하여 여러 줄 일치를 용이하게 하는이 grep 기술 (최신 버전의 GNU grep으로 추측)은 어떻습니까? 제한이 있는지 알고 싶습니까?
iruvar

1
@ 1_CR, NUL 문자가 없으면 행에 NUL 문자가 없다고 가정하면 전체 파일을 메모리에로드합니다. 또한 이전 버전의 GNU grep (OP에 있음)은와 -z함께 사용할 수 없습니다 -P. 더 없다 \N없이 -P당신이 그것을 작성해야 것, $'[\01-\011\013-\0377]'단지 C 로케일에서 일 것이다 (참조 thread.gmane.org/gmane.comp.gnu.grep.bugs/5187 )
스테판 Chazelas가

@StephaneChazelas, 매우 유용한 디테일, 감사
iruvar

2

awk:

awk '{if (p ~ /^C/ && $1 ~ /^C/) print; p=$1}' afile.txt

로 시작하는 연속 된 행이 있으면 파일의 내용을 인쇄합니다 C. 표현식 (p ~ /^C/ && $1 ~ /^C/)은 파일에서 연속적인 행을 살펴보고 두 문자의 첫 문자가 일치하면 true로 평가됩니다 C. 이 경우 줄이 인쇄됩니다.

이러한 패턴을 가진 모든 파일을 찾으려면 다음 명령을 통해 위의 awk를 실행할 수 있습니다 find.

find /your/path -type f -exec awk '{if (p ~ /^C/ && $1 ~ /^C/) {print FILENAME; exit;} p=$1}' {} \;

이 명령에서 find+ exec는 각 파일을 통과 awk하고 각 파일에 대해 유사한 필터링을 수행 FILENAME하고 awk 표현식이 true로 평가되면 이름을 인쇄 합니다. FILENAME일치하는 항목이 여러 개인 단일 파일에 대해 여러 번 인쇄하지 않도록하기 위해 exit명령문이 사용됩니다 (@terdon 덕분에).


제 질문은 충분히 명확하지 않았습니다. 시작하는 줄이 두 줄 이상인 파일 이름을 알고 싶습니다C
Jérémie

@ Jérémie 답변을 업데이트했습니다.
mkc

작동 방식에 대한 설명을 추가해 주시겠습니까? 또한, 거기에 대한 필요가 없으며 flag, 단지 exit대신. 이렇게하면 일치하는 것을 찾은 후에 파일을 계속 처리 할 필요가 없습니다.
terdon

2

GNU의 또 다른 옵션 sed:

단일 파일의 경우 :

sed -n -- '/^C/{n;/^C/q 1}' "$file" || printf '%s\n' "$file"

읽을 수없는 파일도보고합니다.

의 경우 find:

find . -type f ! -exec sed -n '/^C/{n;/^C/q 1}' {} \; -print

읽을 수없는 파일이 인쇄되는 문제는 다음과 같이 작성하면 피할 수 있습니다.

find . -type f -size +2c -exec sed -n '$q1;/^C/{n;/^C/q}' {} \; -print

자세히 설명해 주 sed -n '$q1;/^C/{n;/^C/q}'시겠습니까?
Jérémie

나를 설명 할 사람이 있습니까?
Jérémie

@ Jérémie- $q1패턴을 찾지 못하면 sed가 오류와 함께 강제 종료됩니다. 파일에 문제가 있으면 읽을 수 없거나 깨져서 오류가 발생합니다. 따라서 패턴이 발견되고 인쇄를 위해 전달되는 경우에만 0 종료 상태로 종료됩니다. 부분 /^C/{n;/^C/q은 매우 간단합니다. C로 시작하는 문자열을 찾으면 다음 줄을 읽고 C로 시작하면 종료 상태 0으로 종료됩니다.
돌진

1

파일이 메모리로 읽을 수있을 정도로 작다고 가정합니다.

perl -000ne 'print "$ARGV\n" if /^C[^\n]*\nC/sm' *

설명:

  • - 000: \n\n레코드 구분 기호로 설정 하면 단락 모드를 설정하여 단락 (연속 줄 바꿈으로 구분)을 한 줄로 처리합니다.
  • -ne: 인수로 제공된 스크립트를 -e입력 파일의 각 행에 적용하십시오 .
  • $ARGV : 현재 처리중인 파일입니다
  • /^C[^\n]*\nC/: C줄의 시작 부분에서 일치 하고 ( sm여기에서 작동하는 이유는 아래 수정 자 설명 참조 ) 0 개 이상의 줄 바꾸기가 아닌 문자, 줄 바꿈 및 다른 C가 뒤 따릅니다. 즉,로 시작하는 연속 줄을 찾으십시오 C. * //sm:이 일치 수정자는 다음과 같습니다 ([여기] 참조).

    • m : 문자열을 여러 줄로 취급합니다. 즉, "^"및 "$"를 문자열의 왼쪽과 오른쪽 끝에있는 줄의 시작 또는 끝과 일치시키는 것에서 문자열 내 어디에서나 일치시키는 것으로 변경하십시오.

    • s : 문자열을 한 줄로 처리합니다. 즉, "."를 변경하십시오. 줄 바꿈과 같은 문자를 일치 시키려면 일반적으로 일치하지 않습니다.

다음과 같은 추한 일을 할 수도 있습니다.

for f in *; do perl -pe 's/\n/%%/' "$f" | grep -q 'C[^%]*%%C' && echo "$f"; done

여기에서 perl코드는 개행 문자를 대신 하여 입력 파일에 %%없다고 가정 할 때 %%( 물론 큰 경우 )로 grep시작하는 연속 행과 일치 C합니다.


1

해결책:

( set -- *files ; for f ; do (
set -- $(printf %c\  `cat <$f`)
while [ $# -ge 1 ] ;do [ -z "${1#"$2"}" ] && {
    echo "$f"; break ; } || shift
done ) ; done )

데모:

먼저 테스트 기반을 만듭니다.

abc="a b c d e f g h i j k l m n o p q r s t u v w x y z" 
for l in $abc ; do { i=$((i+1)) h= c= ;
    [ $((i%3)) -eq 0 ] && c="$l" h="${abc%"$l"*}"
    line="$(printf '%s ' $h $c ${abc#"$h"})"
    printf "%s$(printf %s $line)\n" $line >|/tmp/file${i}
} ; done

위의 /tmp이름 은 라는 26 개의 파일을 만듭니다 file1-26. 각 파일에는 문자로 시작 a-z하고 그 뒤에 알파벳의 27 줄 또는 28 줄이 있습니다. 모든 세 번째 파일에는 첫 번째 문자가 복제되는 두 개의 연속 행이 포함됩니다.

견본:

cat /tmp/file12
...
aabcdefghijkllmnopqrstuvwxyz
babcdefghijkllmnopqrstuvwxyz
cabcdefghijkllmnopqrstuvwxyz
...
kabcdefghijkllmnopqrstuvwxyz
labcdefghijkllmnopqrstuvwxyz
labcdefghijkllmnopqrstuvwxyz
mabcdefghijkllmnopqrstuvwxyz
...

그리고 내가 변할 때 :

set -- *files

에:

set -- /tmp/file[0-9]*

나는 얻다...

산출:

/tmp/file12
/tmp/file15
/tmp/file18
/tmp/file21
/tmp/file24
/tmp/file3
/tmp/file6
/tmp/file9

간단히 말해서 솔루션은 다음과 같이 작동합니다.

set 서브 쉘 파일의 모든 positionals하고, 각

set이야 가 루프로 각 파일의 각 행의 첫 글자에 중첩 된 서브 쉘의 positionals을.

[ tests ]경우 $1을 Negate이 $2경기를 나타내는, 만약 그렇다면

echoes파일명은 breaks의 현재 루프 반복

다른 사람 shift 다음 단일 문자 위치에 다시 시도


0

이 스크립트는 및를 사용 grep하여 cut일치하는 줄의 줄 번호를 가져오고 연속 된 두 번호를 확인합니다. 파일은 스크립트에 첫 번째 인수로 전달 된 유효한 파일 이름으로 간주됩니다.

#!/bin/bash

checkfile () {
 echo checking $1
 grep -n -E "^C.*$" $1 | cut -d: -f1 | while read linenum
     do
        : $[ ++PRV ] 
        if [ $linenum == $PRV ]; then return 1; fi
        PRV=$linenum
     done
     return 0
}

PRV="-1"
checkfile $1
if [ $? == 0 ]; then
   echo Consecutive matching lines found in file $1
fi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.