glob에 확장자가 있는지 확인하는 방법은 무엇입니까? [복사]

glob에 확장자가 있는지 확인하는 방법은 무엇입니까? [복사]

이 작업을 수행하는 깨끗하고 전통적인 방법이 있습니까?

예를 들어 특정 디렉터리에 ".gz" 파일이 있으면 압축을 풀고 싶습니다. 하지만 그렇지 않다면 어떤 오류도 보고 싶지 않습니다.

을 사용하면 gzip -d /mydir/*.gz오류가 발생합니다.

gzip: /mydir/*.gz: No such file or directory

shopt -s nullglob먼저 수행 한 다음 을 수행하면 gzip -d /mydir/*.gz다음과 같은 결과를 얻습니다.

gzip: compressed data not read from a terminal. Use -f to force decompression.
For help, type: gzip -h

제가 아는 방법이 효과가 있을 것으로 알고 있으며 이를 답변으로 게시하겠습니다. 더 좋고 깨끗한 방법이 있는지 궁금합니다.

POSIX 호환성은 장점이지만 필수는 아닙니다.

답변1

그리고 bash:

shopt -s nullglob
files=(/mydir/*.gz)
((${#files[@]} == 0)) || gzip -d -- "${files[@]}"

그리고 zsh:

files=(/mydir/*.gz(N))
(($#files == 0)) || gzip -d -- $files

zsh, 없이 Bourne 쉘 이전 의 (N)csh 또는 tcsh와 마찬가지로 glob이 일치하지 않으면 명령이 실행되지 않습니다. 오류 메시지를 피하기 위해 위의 작업을 수행할 수 있습니다(일치하는 항목이 없습니다.Bourne과 같은 쉘 gzip의 경우 확장된 글로브에서 실패하는 것과는 반대로 ). with 를 사용하면 bash동일한 결과를 얻을 수 있습니다.bashshopt -s failglob

에서 zsh실패한 glob은 확장된 셸 프로세스가 비대화식으로 종료되도록 하는 치명적인 오류입니다. 이 경우 서브쉘을 사용하거나 오류 트랩 메커니즘을 사용하여 (또는 물론 권장하지 않는 설정 (예: ) 또는 옵션을 사용하여) zsh스크립트가 종료되는 것을 방지 할 수 있습니다.{ try-block; } always { error-catching; }nonomatchshnullglobnoglob

$ zsh -c 'echo zz*; echo not output'
zsh:1: no matches found: zz*
$ zsh -c '(echo zz*); echo output'
zsh:1: no matches found: zz*
output
$ zsh -c '{echo zz*;} always {TRY_BLOCK_ERROR=0;}; echo output'
zsh:1: no matches found: zz*
output
$ zsh -o nonomatch -c 'echo zz*; echo output'
zz*
output

glob을 외부 명령에 전달하면 명령을 실행하기 위해 분기된 프로세스만 종료됩니다.

$ zsh -c '/bin/echo zz*; echo still output and the previous command exited with $?'
zsh:1: no matches found: zz*
still output and the previous command exited with 1

ksh93으로

ksh93zsh(N)전역적으로 옵션을 설정 하지 않아도 되도록 glob 한정자와 유사한 메커니즘이 결국 추가되었습니다 nullglob.

files=(~(N)/mydir/*.gz)
((${#files[@]} == 0)) || gzip -d -- "${files[@]}"

POSIX적으로

일치하지 않는 glob이 확장되지 않은 채 전달 되는 POSIX에서는 sh해당 동작을 비활성화할 수 있는 방법이 없습니다(유일한 POSIX glob 관련 옵션은 noglobglobbing을 완전히 비활성화하는 것입니다). 요령은 다음을 수행하는 것입니다.

set -- /mydir/[*].gz /mydir/*.gz
case $#$1$2 in
   '2/mydir/[*].gz/mydir/*.gz') : no match;;
   *) shift; gzip -d -- "$@"
esac

/mydir/*.gz일치하는 항목이 없으면 자체적으로 확장된다는 아이디어입니다 ( /mydir/*.gz). 그러나 실제로 호출되는 파일이 있는 경우에도 확장할 수 있으므로 /mydir/*.gz두 경우를 구별하기 위해 glob을 사용합니다. glob /mydir/[*].gz은 그렇게 호출되는 파일이 있는 경우에도 확장됩니다./mydir/*.gz

find이는 어색하므로 다음과 같은 경우에 사용하는 것이 좋습니다 .

find /mydir/. ! -name . -prune ! -name '.*' \
   -name '*.gz' -type f -exec gzip -d {} +

이는 ! -name . -prune하위 디렉토리를 조사하지 않습니다(일부 find구현에는 동등한 기능이 있음 -mindepth 1 -maxdepth 1). ! -name '.*'glob과 마찬가지로 숨겨진 파일을 제외합니다.

find또 다른 이점은 파일 목록이 너무 커서 실행된 명령의 매개변수 크기 제한에 맞지 않는 경우에도 여전히 작동한다는 것입니다( 여러 명령이 실행되는 상황을 피해야 하는 경우 ( with ) 및 ( with )에도 메커니즘이 있습니다. 이 문제를 해결하기 위해).gzipksh93command -xzshzargs

find또 다른 이점은 파일 내용을 읽을 수 없거나 /mydir파일 유형을 확인할 수 없는 경우 오류 메시지가 표시된다는 것입니다 (glob은 자동으로 문제를 무시하고 해당 파일이 존재하지 않는 것처럼 동작합니다).

gzip작은 단점은 종료 상태의 정확한 값을 잃는다는 것입니다 (두 gzip호출 중 하나가 0이 아닌 종료 상태로 실패 하면 find여전히 0이 아닌 종료 상태로 종료됩니다(반드시 동일하지는 않지만). 따라서 대부분의 경우 경우에는 이것으로 충분합니다).

또 다른 이점은 .glob -type f에 대한 이름의 압축을 풀려는 시도를 피하기 위해 추가할 수 있다는 것입니다 ( 일반 파일에만 해당). 다음을 수행해야 합니다..gzzsh*.gz(.)

set --
for f in /mydir/*.gz
  [ -f "$f" ] && [ ! -L "$f" ] && set -- "$@" "$f"
done
[ "$#" -eq 0 ] || gzip -d -- "$@"

답변2

한 가지 방법은 다음과 같습니다.

shopt -s nullglob
for f in /mydir/*.gz; do
  gzip -d /mydir/*.gz
  break
done

fornullglob로 설정된 루프는 glob에 확장이 있는 경우에만 루프를 실행하며 무조건 명령문은 명령이 한 번만 실행되도록 break보장합니다 .gzip

for이것은 루프를 루프로 사용하기 때문에 약간 웃기지 if만 작동합니다.

답변3

가장 간단한 해결책은

for f in *.gz; do
  test -f "$f" && gzip -d -- "$f"
done

또는

for f in *.gz; do
  [ -f "$f" ] && gzip -d -- "$f"
done

심지어

for f in *.gz; do
  if [ -f "$f" ]; then
    gzip -d -- "$f"
  fi
done

코드의 기능을 설명하는 데 도움이 된다면 장황하게 설명하겠습니다. 다양한 쉘은 다양한 작업을 수행하기 위한 간결한 구문을 가지고 있지만 수행 중인 작업을 문서화하는 측면(및 이식성) 측면에서 위의 구문을 능가하지는 않습니다. 나는 여기서 주로 쉘 스크립트와 여러분의 스크립트가 다른 사람들에 의해 사용되고 수정될 가능성에 대해 생각하고 있습니다.

편집하다:

glob에 확장자가 있는지 확인하려면 다음을 수행하세요.

if [ "$(printf '%s' *.gz)" = "*.gz" ] && [ ! -f "*.gz" ]; then
    # From Stéphane Chazelas: "There are no gzipped files that I
    # can tell, or if there's one, it's called *.gz and it is not a
    # regular file (after symlink resolution) or at least I can't tell
    # if it's a regular file or not."

    echo "There are no gzipped files"
else
    echo "There are gzipped files"
fi

아래의 이국적인 파일 이름에 대한 Stéphane의 추가 설명도 참조하세요.

관련 정보