이 작업을 수행하는 깨끗하고 전통적인 방법이 있습니까?
예를 들어 특정 디렉터리에 ".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
동일한 결과를 얻을 수 있습니다.bash
shopt -s failglob
에서 zsh
실패한 glob은 확장된 셸 프로세스가 비대화식으로 종료되도록 하는 치명적인 오류입니다. 이 경우 서브쉘을 사용하거나 오류 트랩 메커니즘을 사용하여 (또는 물론 권장하지 않는 설정 (예: ) 또는 옵션을 사용하여) zsh
스크립트가 종료되는 것을 방지 할 수 있습니다.{ try-block; } always { error-catching; }
nonomatch
sh
nullglob
noglob
$ 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으로
ksh93
zsh
(N)
전역적으로 옵션을 설정 하지 않아도 되도록 glob 한정자와 유사한 메커니즘이 결국 추가되었습니다 nullglob
.
files=(~(N)/mydir/*.gz)
((${#files[@]} == 0)) || gzip -d -- "${files[@]}"
POSIX적으로
일치하지 않는 glob이 확장되지 않은 채 전달 되는 POSIX에서는 sh
해당 동작을 비활성화할 수 있는 방법이 없습니다(유일한 POSIX glob 관련 옵션은 noglob
globbing을 완전히 비활성화하는 것입니다). 요령은 다음을 수행하는 것입니다.
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 )에도 메커니즘이 있습니다. 이 문제를 해결하기 위해).gzip
ksh93
command -x
zsh
zargs
find
또 다른 이점은 파일 내용을 읽을 수 없거나 /mydir
파일 유형을 확인할 수 없는 경우 오류 메시지가 표시된다는 것입니다 (glob은 자동으로 문제를 무시하고 해당 파일이 존재하지 않는 것처럼 동작합니다).
gzip
작은 단점은 종료 상태의 정확한 값을 잃는다는 것입니다 (두 gzip
호출 중 하나가 0이 아닌 종료 상태로 실패 하면 find
여전히 0이 아닌 종료 상태로 종료됩니다(반드시 동일하지는 않지만). 따라서 대부분의 경우 경우에는 이것으로 충분합니다).
또 다른 이점은 .glob -type f
에 대한 이름의 압축을 풀려는 시도를 피하기 위해 추가할 수 있다는 것입니다 ( 일반 파일에만 해당). 다음을 수행해야 합니다..gz
zsh
*.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
for
nullglob로 설정된 루프는 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의 추가 설명도 참조하세요.