디렉토리의 지정된 파일 이름에서만 패턴 / 텍스트를 재귀 적으로 검색 하시겠습니까?


16

나는 디렉토리 (예를 들어,이 abc/def/efg많은 하위 디렉토리 (예를 들어, :로를) abc/def/efg/(1..300)). 이러한 모든 하위 디렉토리에는 공통 파일 (예 :)이 file.txt있습니다. file.txt다른 파일을 제외 하고이 문자열 만 검색하고 싶습니다 . 어떻게해야합니까?

나는을 사용 grep -arin "pattern" *했지만 많은 하위 디렉토리와 파일이 있으면 매우 느립니다.


답변:


21

상위 디렉토리에서 해당 파일 만 사용 find하고 실행할 수 있습니다 grep.

find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +

2
또한 통과 제안 -Hgrep오직 하나의 경로에 전달 될 때 경로가 여전히 (오히려 파일에서 바로 일치하는 라인보다) 인쇄되어, 경우에, 그래서.
Eliah Kagan

24

globstar를 사용할 수도 있습니다.

구축 grep과 명령을 findZANNA의 대답처럼 , (또한 참조이 할 수있는 매우 강력하고 다양한 기능, 휴대용 방법입니다 sudodus의 답변을 ). 그리고 muru가 사용하는 훌륭한 방법 올렸습니다 grep--include옵션을 . 그러나 grep명령과 쉘만 사용하려면 다른 방법 이 있습니다 . 쉘 자체 가 필요한 재귀를 수행하도록 할 수 있습니다 .

shopt -s globstar   # you can skip this if you already have globstar turned on
grep -H 'pattern' **/file.txt

-H플래그 차종은 grep단 하나 일치하는 파일을 찾을 경우에도 파일 이름을 보여줍니다. 필요한 -a경우 -i, 및 -n플래그 (예제에서)도 전달할 수 있습니다 grep. 그러나이 방법을 사용 -r하거나 통과 -R할 때 통과하지 마십시오 . 그것은이다 이 포함 된 글로브 패턴 확장에 디렉토리를 재귀 **하지를grep .

이 지침은 Bash 셸에만 해당됩니다. Bash는 Ubuntu (및 대부분의 다른 GNU / Linux 운영 체제)의 기본 사용자 셸이므로 Ubuntu를 사용 중이고 셸이 무엇인지 모른다면 거의 Bash입니다. 널리 사용되는 쉘은 일반적으로 디렉토리 탐색 **글로브를 지원하지만 항상 같은 방식으로 작동하지는 않습니다. 자세한 정보 는 Unix.SE 에서 ls *, ls ** 및 ls ***의 결과에 대한 Stéphane Chazelas탁월한 답변 을 참조하십시오 .

작동 원리

globstar bash shell 옵션을 켜면 **디렉토리 구분 기호 ( /)가 포함 된 일치 경로가 만들어 집니다. 따라서 디렉토리 재귀 글로브입니다. 구체적으로 man bash설명 하면 다음과 같습니다.

globstar의 쉘 옵션이 활성화되고, * 경로명 확장 컨텍스트에 사용되는 두 개의 인접한 * 모든 파일과 0 개 이상의 디렉토리 및 하위 디렉토리를 일치 하나의 패턴으로 사용 s의. /가 오는 경우, 인접한 두 개의 *는 디렉토리 및 하위 디렉토리에만 일치합니다.

수정하거나 당신이 의도 한 것보다 훨씬 더 많은 파일을 삭제 명령을 실행할 수 있기 때문에 당신이 쓰는, 특히 당신은이 조심해야 **당신이 쓰는 의미 할 때 *. (이 명령에서는 파일을 변경하지 않는 것이 안전합니다.) shopt -u globstarglobstar shell 옵션을 다시 끕니다.

globstar와 사이에는 몇 가지 실질적인 차이점이 find있습니다.

findglobstar보다 훨씬 더 다양합니다. globstar로 할 수있는 모든 것, find명령으로도 할 수 있습니다. 나는 globstar를 좋아하고 때로는 더 편리하지만 globstar는 일반적인 대안 이 아닙니다 find.

위의 방법은 이름이로 시작하는 디렉토리 내부를 찾지 않습니다 .. 때로는 그러한 폴더를 되풀이하고 싶지 않지만 때로는 그렇게합니다.

일반적인 glob와 마찬가지로 쉘은 모든 일치하는 경로 목록을 작성 grep하고 glob 자체 대신 명령 ( )에 인수로 전달합니다 . 호출 된 파일이 너무 많아서 file.txt결과 명령이 시스템을 실행하기에 너무 길면 위의 방법이 실패합니다. 실제로 수천 개 이상의 파일이 필요하지만 그럴 수 있습니다.

사용하는 방법 find에는 다음과 같은 이유로이 제한이 적용되지 않습니다.

  • Zanna의 방법grep잠재적으로 많은 경로 인수 로 명령을 작성하고 실행합니다 . 그러나 단일 경로에 나열 될 수있는 것보다 더 많은 파일이 발견되면 +-terminated -exec조치는 일부 경로로 명령을 실행 한 다음 더 많은 경로로 다시 실행하는 식입니다. 의 경우 grep여러 파일에서 문자열을 보내고,이 올바른 동작을 생성합니다.

    여기에서 다루는 globstar 방법과 같이 경로 앞에 각각의 경로가있는 일치하는 모든 줄을 인쇄합니다.

  • sudodus의 길은grepfile.txt발견 에 대해 별도로 실행 됩니다 . 파일이 많은 경우 다른 방법보다 속도가 느릴 수 있지만 작동합니다.

    이 방법은 파일을 찾고 경로를 인쇄 한 다음 일치하는 줄이 있으면 인쇄합니다. 이것은 내 방법, Zanna 'smuru ' s 에서 생성 한 형식과 다른 출력 형식입니다 .

와 함께 색상 얻기 find

globstar를 사용하면 즉각적인 이점 중 하나는 기본적으로 Ubuntu에서 grep색상이 지정된 출력물이 생성된다는 것입니다. 그러나 당신은 쉽게 이것을 얻을 수있는 find .

사용자는 우분투가 만들어집니다에 계정 별칭 하게 grep정말 실행 grep --color=auto(실행 alias grep참조). 그건 좋은 일이 별칭이되는 거의 유일한 대화 형을 발행 할 때 확장 ,하지만 당신이 원한다면 것을 의미 find호출 grep--color플래그, 당신은 명시 적으로 작성해야합니다. 예를 들면 다음과 같습니다.

find . -name file.txt -exec grep --color=auto -H 'pattern' {} +

bash이 작업을 수행 하려면 셸을 사용해야한다는 것을보다 명확하게 설명 할 수 있습니다 . 당신은 않는다 "는 globstar의 bash 쉘 옵션"에서 암시 적으로 그 말을하지만, 쉽게 너무 빨리 읽는 사람들이 놓칠 수 있습니다.
Stig Hemmer

중요한 의견이 많았 기 때문에 답변을 삭제했습니다. 따라서 답변에서 참조를 제거해야합니다.
sudodus

@ StigHemmer 감사합니다-모든 쉘 에이 기능이있는 것은 아닙니다. 많은 쉘 (단지 배쉬가) 지원 디렉토리 통과 할 수 있지만 **globs의를 핵심 비판은 정확 :의 프리젠 테이션 **이 대답은 shopt 내부 인 배쉬와 함께, 떠들썩한 파티에 고유 한 용어 "globstar"인 (내가 생각하는) 배쉬와 tcsh 만 해당 나는 그 복잡성 때문에 원래 이것에 대해 글을 썼지 만 다소 혼란 스럽다는 것이 맞습니다. 이 답변에서 길게 논의하기보다는 무거운 리프팅을 수행하는 다른 (완전히 철저한) 게시물에 연결했습니다.
Eliah Kagan

@ sudodus 나는 그렇게했지만 일시적인 것이기를 바랍니다. 저와 다른 사람들이 당신의 대답을 소중하게 여겼습니다. 그것은 사실 -e경로에 적용되어서는 안된다, 그러나 이것은 쉽게 고정됩니다. 첫 번째 명령의 경우 생략하십시오 -e. 두 번째로 find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;또는을 사용하십시오 find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;. 사용자는 때때로 (와 당신의 방법을 선호 -e하나 개의 경로 인쇄 다른 사람에게 사용 고정) 일치하는 한 줄을 ; 찾은 파일 당 하나의 경로를 인쇄 다음 grep결과를 출력합니다.
Eliah Kagan

@sudodus 그래서 grep그 자체로 당신이하고있는 일을하지 않습니다 . 다른 비판도 잘못되었습니다. grep -H로 실행 -exec하면 --color(또는 GREP_COLOR) 없이 색상이 표시되지 않습니다 . IEEE 1003.1-2008은의{} 확장을 보장하지는 ##### {}:않지만 우분투에는 GNU find가 있습니다. 괜찮다 면 게시물을 수정하여 -e버그 를 수정 하고 사용 사례를 명확히하고 삭제 취소 여부를 확인할 수 있습니다. (삭제 된 게시물을 보거나 편집 할 담당자가 있습니다.)
Eliah Kagan

18

당신은 이것을 필요 find로 하지 않습니다 ; grep자체적으로 완벽하게 처리 할 수 ​​있습니다.

grep "pattern" . -airn --include="file.txt"

보낸 사람 man grep:

--exclude=GLOB
      Skip  files  whose  base  name  matches  GLOB  (using   wildcard
      matching).   A  file-name  glob  can  use  *,  ?,  and [...]  as
      wildcards, and \ to quote  a  wildcard  or  backslash  character
      literally.

--exclude-from=FILE
      Skip  files  whose  base name matches any of the file-name globs
      read from FILE  (using  wildcard  matching  as  described  under
      --exclude).

--exclude-dir=DIR
      Exclude  directories  matching  the  pattern  DIR from recursive
      searches.

--include=GLOB
      Search  only  files whose base name matches GLOB (using wildcard
      matching as described under --exclude).

좋습니다-이것이 최선의 방법 인 것 같습니다. 간단하고 효율적입니다. 이 방법에 대해 알고 있었거나 맨 페이지를 확인하려고 생각했습니다. 감사!
Eliah Kagan

@EliahKagan Zanna가이 게시물을 게시하지 않은 것에 대해 더 놀랐습니다. 얼마 전에 다른 답변에 대한이 옵션의 예를 보여주었습니다. :)
muru

2
느린 학습자, 아아, 그러나 나는 결국 거기에 도착한다, 당신의 가르침은 나에게 완전히 낭비되지 않았다;)
Zanna

이것은 매우 간단하고 기억하기 쉽습니다. 감사합니다.
Rajesh Keladimath

이것이 최선의 답변이라는 데 동의합니다. 혼동을 줄이려면 대답을 제거하거나 대안이 있다는 것을 보여 주어야하며 어떻게해야하는지find?
sudodus

8

파일 이름을 지정하기 위해 플래그로 실행하는 muru의 답변에 제공된 방법 이 종종 최선의 선택입니다. 그러나이 작업을 수행 할 수도 있습니다 .grep--includefind

이 답변의 접근 방식은 찾은 각 파일에 대해 별도로 find실행 grep하고 각 파일에서 찾은 일치하는 줄 위에 각 파일의 경로를 정확히 한 번 인쇄 합니다. (모든 일치하는 줄 앞에 경로를 인쇄하는 방법은 다른 답변에서 다룹니다.)


디렉토리를 해당 파일이있는 디렉토리 트리의 맨 위로 변경할 수 있습니다. 그런 다음 다음을 실행하십시오.

find . -name "file.txt" -type f -exec echo "##### {}:" \; -exec grep -i "pattern" {} \;

그러면 .이름이 지정된 각 파일 의 경로 (현재 디렉토리를 기준으로하고 파일 이름 자체 포함)와 파일의 file.txt모든 일치하는 행이 인쇄됩니다. {}찾은 파일의 자리 표시 자 이므로 작동 합니다. 각 파일의 경로는 접두사가 붙음으로써 내용과 별도로 설정 #####되며 해당 파일의 일치하는 줄 앞에 한 번만 인쇄됩니다. file.txt일치하는 항목이없는 호출 된 파일 에는 여전히 경로가 인쇄되어 있습니다. 모든 일치하는 줄의 시작 부분에 경로를 인쇄하는 메소드에서 얻는 것보다이 출력이 어수선하지 않을 수 있습니다.

올바른 이름의 파일을 검색하고 다른 모든 파일을 건너 뛰기 때문에 find이와 같이 사용 하면 거의 항상 모든 파일 ( )에서 실행 grep하는 것보다 빠릅니다 .grep -arin "pattern" *find

우분투는 GNU 찾기를 사용 하는, 항상 확장 {}은 더 큰 문자열에 표시되는 경우에도 같은 ##### {}:. 이 기능을 지원하지 않는 시스템 에서 작업find 할 명령이 필요 하거나 -exec꼭 필요한 경우에만 작업 을 사용하려는 경우 다음을 사용할 수 있습니다.

find . -name "file.txt" -type f -printf '##### %p:\n' -exec grep -i "pattern" {} \;

출력을보다 쉽게 ​​읽을 수 있도록 ANSI 이스케이프 시퀀스를 사용하여 색상이 지정된 파일 이름을 얻을 수 있습니다. 이렇게하면 각 파일의 경로 머리글이 그 아래에 인쇄되는 일치하는 줄에서 더 잘 나타납니다.

find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;

그러면 이 녹색 의 이스케이프 코드 를 터미널에서 녹색을 생성하는 실제 이스케이프 시퀀스로 바꾸고 일반 색상의 이스케이프 코드와 동일한 작업을 수행하게됩니다. 이 이스케이프는로 전달되어 find파일 이름을 인쇄 할 때 사용됩니다. ( 의 조치가 ANSI 이스케이프 코드 해석을 인식하지 못 $' '하므로 여기에 인용이 필요합니다 .)find-printf\e

원하는 경우 시스템 명령 (을 지원하는 ) -exec과 함께 사용할 수 있습니다 . 따라서 동일한 작업을 수행하는 다른 방법은 다음과 같습니다.printf\e

find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;

배열로 "for loop"를 만들려고했는데 find에서 exec native 옵션에 대해 생각하지 않았습니다. 좋은 것! 그러나 도트를 사용하면 이미있는 디렉토리에서 당신을 찾을 것이라고 생각합니다. 내가 틀렸다면 나를 바로 잡으십시오. 찾기 순서로 구문 분석 할 직접을 지정하는 것이 더 좋지 않습니까? find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
kcdtv

물론, cd abc/def/efg'change directory'명령 을 제거 할 것입니다 :-)
sudodus

(1) 왜 -e옵션을 echo? 그러면 백 슬래시가 포함 된 파일 이름이 엉망이됩니다. 사용 (2) {}으로 의 부분 인수를 사용할 수 있다고 보장 할 수는 없습니다. -exec echo "#####" {} \;또는 말하는 것이 좋습니다 -exec printf "##### %s:\n" {} \;. (3) 왜 그냥 사용하지 -print-printf? (4) 또한 고려하십시오 grep -H.
G-Man, 'Reinstate

@ G-man, 1) 원래 ANSI 색상을 사용했기 때문에 find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;2) 당신이 옳을 수도 있지만 지금까지 이것은 나를 위해 일하고 있습니다. 3) -print 및 -printf도 대안입니다. 4) 이것은 이미 주요 답변에 있습니다. -어쨌든, 당신은 당신의 자신의 답변에 오신 것을 환영합니다 :-)
sudodus

-exec통화 가 필요하지 않습니다 . 사용 grep -H하면 일치하는 텍스트뿐만 아니라 파일 이름 (색상)이 인쇄됩니다.
terdon

0

질문의 조건을 문학적으로 받아 들일 수 있다면 직접 grep을 사용할 수 있다고 지적하십시오.

grep 'pattern' abc/def/efg/*/file.txt

또는

grep 'pattern' abc/def/efg/{1..300}/file.txt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.