"ls *.e*"를 "find -execdir"의 매개변수로 사용할 수 없는 이유는 무엇입니까?

"ls *.e*"를 "find -execdir"의 매개변수로 사용할 수 없는 이유는 무엇입니까?

*.e*다른 파일( )과 같은 디렉토리에 있는 파일( )을 찾으려고 합니다 md.tpr. 추가 처리를 위해 다음을 사용하여 해당 항목을 나열해야 합니다.

find . -name md.tpr -execdir ls *.e* \;

나는 이 명령의 몇 가지 변형과 다른 몇 가지 변형을 시도했습니다(명령에 작은따옴표 전달 또는 몇 가지 이름을 지정하기 위해 전달 -execdir하는 것을 포함). 와일드카드는 또는 에 전달될 때 작동하지 않는 것 같습니다 . 위 명령을 실행할 때 발생하는 오류는 다음과 같습니다.sh -c 'ls *.e*'eval 'ls *.e*'-exec-execdir

ls: *.e*: No such file or directory

온전성 검사와 마찬가지로 인쇄해야 할 내용이 인쇄되었으므로 해당 테스트에서 나열된 디렉터리에 파일이 존재하므로 -execdir pwd와일드카드 문제인 것처럼 보입니다 .*.e*

이제 덜 우아한 방식으로 이 문제를 해결할 수 있었지만 여기서는 왜 와일드카드와 와일드카드가 작동하지 않는지 혼란스러울 뿐입니다. 어떤 아이디어가 있나요? 아니면 내가 완전히 궤도를 벗어난 걸까?

저는 bash 3.2.25(이전 버전이지만 해당 시스템에 대한 관리자 권한이 없음)를 사용하고 있습니다.

또한 흥미롭게도 내가 그렇게 한다면

find ~ -name .bashrc -maxdepth 2 -execdir ls -d .b* \;

에서 완료하지 않으면 작동하지 않습니다 $HOME.

답변1

find 명령과 쉘 모두 가능파일 글로빙.

이는 특이한 현상입니다. 대부분의 명령은 와일드카드를 사용할 수 없으며 와일드카드를 확장하기 위해 전적으로 쉘에 의존합니다. 하지만 find는 슈퍼 슈퍼 슈퍼 사용자 도구이고 이로 인해 쉽게 다칠 수 있습니다!

예: 명령을 실행할 때

   find /path -iname *.txt

가장 먼저 일어나는 일은 쉘이 현재 디렉토리에서 *.txt와 일치하는 모든 파일을 찾으려고 시도한다는 것입니다. 발견되면 다음 이름으로 대체됩니다.모두glob을 찾은 다음 find 명령을 호출하십시오. 찾기 명령결코 큰 그림을 보지 마라이런 일이 발생하면 쉘은 이를 존재하지 않는 것으로 확장했습니다.

그러나 현재 디렉토리에 glob과 일치하는 파일이 없으면 쉘은 어깨를 으쓱하고 다음변경되지 않은 글로브 전달찾다. 따라서 이 시점에서 find 명령(glob을 이해한다는 점을 기억하세요)은 glob과 일치하는 /path에서 찾은 모든 파일의 이름을 인쇄합니다.

따라서 이러한 방식으로 glob을 사용한다는 것은 find가 현재 디렉터리의 내용에 따라 다르게 동작한다는 것을 의미합니다. 이것은 거의 확실히 당신이 원하는 것이 아닙니다!

find가 글로브를 보기 전에 쉘이 글로브를 조작하는 것을 방지하려면,glob을 이스케이프하려면 적절한 쉘 메타 문자 인용을 사용하십시오. 일반적으로 이는 글로브스트링을 다음과 같이 작은따옴표로 묶는 것을 의미합니다.

   find /path -iname '*.txt'

GLOBS는 정규 표현식이 아니라는 점을 기억하십시오. glob ".*"와 정규 표현식 ".*"은 매우 다르며 동일한 문자열과 일치하지 않습니다!

답변2

.b*따옴표가 없는 전역 변수(예: 또는 )가 포함된 명령을 입력하면 *.e*셸이 해당 명령을 확장합니다. 나는 전에 이런 일이 일어나는 것을 본 적이 있습니다 find.

디렉토리에 등의 파일이 있을 수 있습니다 .bashrc. 따라서 다른 곳에서 실행하면 명령은 가 됩니다. 다른 곳에서 실행하면 해당 glob은 어떤 것과도 일치하지 않으므로 전달됩니다. 전 세계적으로 아무 것도 수행하지 않으므로 여전히 작동하지 않습니다. glob을 확장하려면 셸을 호출해야 합니다..bash_history$HOME$HOMEfind ... -execdir ls -d .bashrc .bash_history ... \;.b*find -exec*-exec

find ... -execdir sh -c 'echo globs: *' \;

답변3

bash쉘이 주어진 와일드카드 패턴과 일치하는 파일을 찾을 수 없으면 패턴을 확장되지 않은 상태로 둡니다 . 이로 인해 find명령이 ls확장되지 않은 패턴을 *.e*인수로 갖게 됩니다. 유틸리티 ls자체는 파일 이름 와일드카드 패턴을 확장하지 않지만 이를 수행하려면 셸을 사용합니다.

이는 홈 디렉토리에서만 제대로 작동하는 것처럼 보이는 마지막 명령의 문제일 가능성이 높습니다. find홈 디렉토리에 패턴과 일치하는 파일( .bashrc예: match .b*)이 포함되어 있기 때문일 수 있습니다. 패턴이 현재 디렉터리의 어떤 것과도 일치하지 않으면 패턴은 ls그대로 전달되며, ls와일드카드 패턴 자체는 확장되지 않으므로 어떤 파일도 나열할 수 없습니다.

즉, ls직접 -execdir또는 호출을 사용하여 -exec파일 이름 글로빙 패턴을 제공할 수 없습니다.

너도 원한다고 했잖아목록*.e*"추가 처리"와 일치하는 파일입니다. 이 작업을 수행하는 대신 find실제 명령 자체에서 수행하는 것이 좋습니다 . 그 이유는 질문/답변에 나와 있습니다.찾기 결과를 반복하는 것이 왜 나쁜 습관입니까?".

그러니 지금 무엇을 하고 있는지 생각하지 마세요.

find . -type f -name md.tpr -exec bash -O nullglob -O dotglob -c '
    for pathname do
        dirpath=${pathname%/md.tpr}
        for e in "$dirpath"/*.e*; do
            # process "$e" here!
        done
    done' bash {} +

이는 md.tpr일반 파일을 찾아야 한다고 가정합니다. 이 find명령은 이러한 모든 파일의 경로 이름을 찾아 일괄적으로 md.tpr인라인 bash스크립트에 제공합니다. 스크립트는 bash매우 짧습니다.

for pathname do
    dirpath=${pathname%/md.tpr}
    for e in "$dirpath"/*.e*; do
        # process "$e" here!
    done
done

이는 단순히 주어진 인수를 취하고, 각 디렉토리의 구성 요소를 추출하고( 우리가 알고 있는 접미사 문자열을 경로 이름에 제거하여) 각 디렉토리에서 일치하는 파일을 /md.tpr반복합니다 ( 각 일치하는 파일의 경로 이름을 차례로 저장).*.e*$e

인라인 스크립트는 패턴이 일치하지 않으면 완전히 제거되고 패턴이 숨겨진 이름과 일치하도록 nullglobdotglob옵션이 설정된 상태 로 실행됩니다.*.e*

-execwith 사용에 대한 자세한 내용 find은 ""find"의 -exec 옵션 이해".


이 질문에 태그를 지정했으므로, 일반 루프에서 동일한 작업을 수행하는 방법은 다음과 같습니다 bash( bash버전 4 이상이 필요함).

shopt -s globstar nullglob dotglob

for pathname in ./**/md.tpr; do
    dirpath=${pathname%/md.tpr}
    for e in "$dirpath"/*.e*; do
        # process "$e" here!
    done
done

이는 일치하는 파일이 일반 파일인지 확인하지 않는다는 점을 제외하면 md.tpr위에서 호출한 인라인 스크립트와 매우 유사해 보입니다 find. 쉘 globstar옵션은 하위 디렉토리를 "재귀적으로" 일치시키는 glob을 bash활성화합니다 .**

를 사용하는 것보다 약간 느릴 것으로 예상되지만 find코드를 작성하는 데 더 편리한 방법일 수 있습니다.

관련 정보