[bash] 현재 디렉토리의 모든 파일을 재귀 적으로 확장하는 것은 무엇입니까?

**/*.ext일치하는 모든 하위 디렉토리의 모든 파일로 확장된다는 것을 알고 *.ext있지만 현재 디렉토리 에있는 모든 파일을 포함하는 유사한 확장은 무엇 입니까?



답변

이것은 Bash 4에서 작동합니다.

ls -l {,**/}*.ext

이중 별표 glob이 작동하려면 globstar옵션을 설정해야합니다 (기본값 : on).

shopt -s globstar

에서 man bash:

    글롭 스타
                  설정하면 파일 이름 확장에 사용되는 ** 패턴이 연결됩니다.
                  텍스트는 파일 및 0 개 이상의 디렉토리와 일치하며
                  하위 디렉토리. 패턴 뒤에 /가 있으면
                  디렉터리와 하위 디렉터리가 일치합니다.

이제 globstar 처리에 버그가 있었는지 궁금합니다. 이제 단순히 사용하여 ls **/*.ext올바른 결과를 얻고 있기 때문 입니다.

그럼에도 불구하고 kenorb가 VLC 저장소를 사용하여 수행분석을 살펴본 결과 해당 분석과 바로 위의 대답에서 몇 가지 문제를 발견했습니다.

find지정 -type f에는 다른 파일 유형 (특히 디렉토리)이 포함되지 않고 ls나열된 명령이 포함될 가능성이 있으므로 명령 의 출력에 대한 비교 는 유효 하지 않습니다 . 또한 나열된 명령 중 하나는 ls -1 {,**/}*.*위의 내 것을 기반으로하는 것처럼 보이지만 하위 디렉터리에있는 파일에 대해 점이 포함 된 이름 만 출력합니다 . OP의 질문과 내 대답에는 특정 확장자를 가진 파일이 있기 때문에 점이 포함됩니다.

그러나 가장 중요한 것은 lsglobstar 패턴과 함께 명령을 사용하는 데 특별한 문제가 있다는 것입니다 **. 패턴이 Bash에 의해 검사되는 트리의 모든 파일 이름 (및 디렉토리 이름)으로 확장되기 때문에 많은 중복이 발생합니다. 확장 후에 ls명령은 각각 과 디렉토리 인 경우 내용을 나열 합니다 .

예:

현재 디렉토리에는 하위 디렉토리 A와 그 내용이 있습니다.

A
└── AB
    └── ABC
        ├── ABC1
        ├── ABC2
        └── ABCD
            └── ABCD1

해당 트리에서 **“AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1″(7 개 항목)으로 확장됩니다. . 그렇게 echo **하면 정확한 출력을 얻을 수 있으며 각 항목은 한 번 표시됩니다. 그러나 그렇게 ls **하면 항목 의 목록이 출력 됩니다. 그래서 본질적 ls A으로 ls A/AB, 등 이 뒤 따르 므로 A/AB두 번 표시됩니다. 또한 ls각 하위 디렉토리의 출력을 별도로 설정합니다.

...
<blank line>
directory name:
content-item
content-item

따라서 using wc -l은 모든 빈 줄과 디렉토리 이름 섹션 제목을 계산하여 훨씬 더 많이 계산합니다.

이것은 파싱ls 하지 말아야하는 또 다른 이유 입니다.

이 추가 분석의 결과로 다음과 같은 방식으로 파일 트리를 반복하는 것 외에는 어떤 상황에서도 globstar 패턴을 사용하지 않는 것이 좋습니다.

for entry in **
do
    something "$entry"
done

최종 비교를 위해 내가 편리했던 Bash 소스 저장소를 사용하여 다음과 같이했습니다.

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

내가 사용하는 tr어떤 이름은 공백을 포함하지 않기 때문에 여기에만 유효 줄 바꿈에 공간을 변경할 수 있습니다. 에서 출력의 각 줄 sed에서 선행을 제거하는 데 사용 되었습니다 . 일반적으로 정렬되지 않았고 Bash의 glob 확장이 이미 정렬되어 있기 때문에 출력을 정렬했습니다. 당신이 볼 수 있듯이,의 유일한 출력은 현재 디렉토리이었다 에 의해 출력 . 내가 할 때 출력에는 거의 두 배의 라인이 있습니다../findfinddiff.findls ** | wc -l


답변

그러면 현재 디렉토리와 ‘.ext’로 끝나는 하위 디렉토리의 모든 파일이 인쇄됩니다.

find . -name '*.ext' -print


답변

다음 **/*.*을 사용 하여 모든 파일을 재귀 적으로 포함 할 수 있습니다 (활성화 🙂 shopt -s globstar.

아래에서 다른 변형 및 작동 방식을 테스트하십시오.


샘플 VLC 저장소 폴더 에 3472 파일이있는 테스트 폴더 :

(총 당으로 계산 3472의 파일 : find . -type f | wc -l)

  • ls -1 **/*.* -3338 반환
  • ls -1 {,**/}*.*-3341을 반환합니다 ( Dennis가 제안한대로 ).
  • ls -1 {,**/}* -8265 반환
  • ls -1 **/*-숨겨진 파일을 제외하고 7817을 반환합니다 ( Dennis가 제안한대로 ).
  • ls -1 **/{.[^.],}*-7869를 반환합니다 ( Dennis가 제안한대로 ).
  • ls -1 {,**/}.?* -15855 반환
  • ls -1 {,**/}.* -20321 반환

따라서 모든 파일을 재귀 적으로 나열하는 가장 가까운 방법 **/*.*gniourf-gniourf 주석 (파일에 적절한 확장자가 있다고 가정하거나 특정 확장자를 사용한다고 가정 )에 따른 첫 번째 예제 ( ) 라고 생각합니다. 두 번째 예제에서는 아래와 같이 중복 항목이 거의 없습니다. :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

다른 하나는 더 많은 중복을 생성합니다.


숨겨진 파일을 포함하려면 다음을 사용하십시오. shopt -s dotglob(으로 비활성화 shopt -u dotglob). mv또는 같은 명령에 영향을 미칠 rm수 있고 실수로 잘못된 파일을 제거 할 수 있으므로 권장되지 않습니다 .


답변

$ find . -type f

현재 디렉토리에있는 모든 파일이 나열됩니다. 그런 다음 -exec를 사용하여 출력에서 ​​다른 명령을 수행 할 수 있습니다.

$find . -type f -exec grep "foo" {} \;

그러면 문자열 “foo”에 대한 검색에서 각 파일을 grep합니다.


답변

중괄호 확장을 사용하여 현재 디렉토리도 포함하지 않는 이유는 무엇입니까?

./{*,**/*}.ext

중괄호 확장은 glob 확장 전에 발생하므로 이전 버전의 bash에서 원하는 작업을 효과적으로 수행 할 수 있으며 최신 버전에서 globstar를 사용하여 원숭이 작업을 방지 할 수 있습니다.

또한 bash ./에서는 glob 패턴에 선행을 포함하는 것이 좋습니다 .


답변