sh 구문을 사용하여 와일드 카드와 일치하는 0 개의 파일을 처리 할 수 ​​있습니까?


0

나는이 편지를 쓰고 싶다. /bin/sh 와일드 카드와 일치하는 모든 파일을 처리하는 쉘 스크립트. 하나 이상의 일치하는 파일을 처리하기 쉽습니다. 그러나 일치하는 파일이 0 개 인 경우를 처리하는 것이 좋지 않습니다.

분명한 구조는 다음과 같습니다.

#!/bin/sh
for f in *.ext; do
  handle "$f"
done

어디에 *.ext 쉘이 파일 경로와 비교하는 하나 이상의 표현식 일 수 있습니다. handle 기존 파일에 대한 경로가 주어지면 올바르게 실행되는 쉘 명령이지만 파일에 맵핑되지 않는 경로를 제공하면 실패합니다. (이 질문을 유발하는 경우, 그들은 *.flacffmpeg,하지만 그건 중요하지 않다고 생각합니다.)

일치하는 파일이있는 경우 foo.ext, bar.ext,이 스크립트는 다음을 수행합니다.

handle "foo.ext"
handle "bar.ext"

예상대로 그러나, 아니 일치하는 파일이면이 스크립트는 다음과 같은 오류 메시지를 표시합니다.

handle: *.ext: No such file or directory

나는 이것이 왜 일어나는 지 이해할 수 있다고 생각합니다. 세게 때리다 맨 페이지 (나는 /bin/sh 또한) "다음 단어 목록 in 항목 목록을 생성합니다. ... 다음 항목을 확장하면 결과가 빈 목록으로 나타나고 명령이 실행되지 않고 반환 상태는 0입니다. "분명히 *.ext "확장 됨", 결과 목록에는 다음이 포함됩니다. *.ext, 빈 목록 대신. 그러나 그것은 그 일을 어떻게 멈추게 하는지를 설명하지 못합니다.

(업데이트 : sh (1) 가보 프로젝트의 맨 페이지 Bourne Shell에 대해 설명합니다. sh, 나중에 Bourne Again 쉘 bash. 아래에 파일 이름 생성 패턴과 일치하는 파일 이름이 없으면 단어는 변경되지 않습니다. "라고 분명하게 말합니다. 왜 sh 잎을 *.ext 패턴 목록에 있습니다.)

작고, 관용적 인 방법으로이 루프를 작성하여 일치하는 파일이없는 경우 오류없이 0 번 실행되지만 일치하는 각 파일에 대해 한 번 실행됩니다. 더 나은 방법은 다음과 같은 여러 패턴을 사용하는 것입니다.

for f in *.ext1 special.ext1 *.ext2; do ...

나는 문법 호환을 선호한다. /bin/sh, 이식성. 필자는 Mac OS X 10.11.6을 사용하고 있지만, 유닉스와 비슷한 OS에서 작동하는 구문을 기대하고 있습니다.

그런 루프를 작성하기 위해 서투른, 비 관용적 인 두 가지 방법을 생각해 냈습니다. 만약 내가 좋은 답변을 즉시 얻지 못한다면, 나는 그 답을 기록으로 남길 것입니다.


bash로 이동하려는 경우 처리하는 것은 쉽지 않습니다.
Ignacio Vazquez-Abrams

@ IgnacioVazquez-Abrams 나는 고집하는 것을 선호한다. /bin/sh, 그러나 그것은 절대적이지 않습니다. "bash로 전환 한 다음이 작업을 수행하십시오"라는 형식의 대답이 도움이된다고 생각합니다. 그것은 "sh에서 그것을하는 방법이 있습니다"라고 말하는 것만 큼 좋지는 않을 것입니다. 그러나 아마 그러한 대답은 존재하지 않을 것입니다.
Jim DeLaHunt

배쉬가있다. nullglob루프가 실행되지 않게됩니다.
Ignacio Vazquez-Abrams

큰! 그것을 좋은 대답으로 바꾸면 점수를 얻게됩니다.
Jim DeLaHunt

답변:


2

이것을 수행하는 가장 간단한 이식 방법은 확장이 실제로 존재하는 것이 없으면 루프를 건너 뛰는 것입니다.

for f in *.ext1 *.ext2; do
  [ -e "$f" ] || continue
  handle "$f"
done

설명 : .ext2 파일이 여러 개 있지만 .ext1 파일이 없다고 가정하십시오. 이 경우 와일드 카드는 *.ext1 filea.ext2 fileb.ext2 filec.ext2. 그래서 [ -e "*.ext1" ] 실패하고 실행됩니다. continue, 루프의 나머지 반복을 건너 뜁니다. 그때 [ -e "filea.ext2" ] 등이 성공하고 루프의 반복이 정상적으로 실행됩니다.

BTW, 예를 들면 다음과 같이 수정할 수 있습니다. [ -f "$f" ] || continue 일반 파일이 아닌 것을 건너 뜁니다 (디렉토리 등을 건너 뛰고 싶다면).

물론 bash에서 실행중인 경우에는 shopt -s nullglob 먼저 목록에 일치하지 않는 패턴을 남기지 않습니다. 그리고 shopt -u nullglob 이후 예기치 않은 부작용 (예 : grep pattern *.nonexistent if stdin에서 읽으려고 시도합니다. nullglob 설정 됨).


현명한 사용 continue, 훌륭한 설명. 나는 nullglob 내가 제대로 이해한다면 두 번째 대답으로 바뀌어. @ 이그나시오 바스케스 - 아브라함 (Abrahams)은이 질문에 대해 언급했다.
Jim DeLaHunt

0

Bourne Again 셸로 전환 ( /bin/bash )에서 Bourne 쉘 ( /bin/sh ), 간단한 해결책이 가능해진다.

그만큼 bash(1) 맨 페이지 ~을 언급하다 널 글로브 선택권:

설정하면 bash는 파일과 일치하지 않는 패턴을 허용합니다 ( 경로명 확장 위의)를 사용하여 자체가 아닌 null 문자열로 확장합니다.

그만큼 경로명 확장 섹션은 말합니다 :

단어 분리 후, bash는 문자에 대한 각 단어를 스캔합니다. * , ? , 및 [ . 이러한 문자 중 하나가 나타나면 해당 단어는 패턴으로 간주되며 패턴과 일치하는 알파벳순으로 정렬 된 파일 이름 목록으로 바뀝니다. 일치하는 파일 이름이없고 쉘 옵션 널 글로브 가 사용 가능하지 않으면 단어는 변경되지 않습니다. 만약 널 글로브 옵션이 설정되고 일치하는 항목이 없으면 단어가 제거됩니다.

그래서, 널 글로브 옵션은 패턴에 대해 원하는 동작을 제공합니다. 패턴과 일치하는 파일이 없으면 루프가 실행되지 않습니다.

#!/bin/bash
shopt -s nullglob
for f in *.ext; do
  handle "$f"
done

하지만 널 글로브 옵션은 다른 명령에 대해 원하는 동작을 방해 할 수 있습니다. 따라서 기존 설정을 저장하는 것이 현명 할 것입니다. 널 글로브 , 나중에 복원하십시오. 다행히도 shopt -p builtin 명령은 입력으로 재사용 될 수있는 양식으로 출력을 보냅니다. 하지만 모든 옵션에 대해 출력을 내고 있으므로 grep 선택하기 누보 환경. 아래 로그를 참조하십시오 (여기서 $ bash 명령 프롬프트를 나타냄).

$ shopt -p | grep nullglob
shopt -u nullglob
$ shopt -s nullglob
$ shopt -p | grep nullglob
shopt -s nullglob

따라서 와일드 카드 패턴과 일치하는 0 개 이상의 파일을 처리하는 최종 bash 구문은 다음과 같습니다.

#!/bin/bash
SAVED_NULLGLOB=$(shopt -p | grep nullglob)
shopt -s nullglob
for f in *.ext; do
  handle "$f"
done
eval "$SAVED_NULLGLOB"

또한 질문은 다음과 같이 여러 패턴을 언급했습니다.

for f in *.ext1 special.ext1 *.ext2; do ...

그만큼 널 글로브 옵션은 "패턴"이 아닌 목록의 단어에 영향을 미치지 않으므로 special.ext1 여전히 루프에 전달됩니다. 이것에 대한 유일한 해결책은 @ 고든 데이비슨 '에스 continue 표현, [ -e "$f" ] || continue .

수신 확인 : 둘 다 @ 이그나시오 바스케스 - 에이 브람스 @ 고든 데이비슨 ~에 암시하는 bash(1) 그리고 그것의 널 글로브 선택권. 고맙습니다. 그들은 완전히 작동 된 예제를 사용하여 즉시 응답을 제공하지 않았기 때문에 스스로 그렇게하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.