Bash와 Dash를 사용하면 셸을 사용하여 빈 디렉터리를 확인할 수 있습니다(단순화를 위해 도트 파일은 무시).
set *
if [ -e "$1" ]
then
echo 'not empty'
else
echo 'empty'
fi
그러나 나는 최근 이 경우 Zsh가 실패한다는 것을 알게 되었습니다.
% set *
zsh: no matches found: *
% echo "$? $#"
1 0
따라서 명령이 set
실패할 뿐만 아니라 설정도 되지 않습니다 $@
. $#
is 인지 테스트할 수 있다고 생각했지만 0
Zsh가 실행을 중단한 것 같습니다.
% { set *; echo 2; }
zsh: no matches found: *
Bash 및 Dash와 비교:
$ { set *; echo 2; }
2
bash, dash 및 zsh에서 작동하는 방식으로 이 작업을 수행할 수 있습니까?
답변1
이것에는 몇 가지 문제가 있습니다
set * if [ -e "$1" ] then echo 'not empty' else echo 'empty' fi
암호:
- 이
nullglob
옵션이 활성화되면(이전에는 대부분의 다른 쉘에서 지원됨) 모든 쉘 변수(및 일부 쉘의 함수)가 대신 나열됩니다.zsh
set *
set
- 숨김이 아닌 첫 번째 파일 이름이
-
또는 로 시작하는 경우+
옵션으로 처리됩니다set
. zsh 및 ksh 의 일부 구현/버전에서는+A[$(reboot)]
.set -- *
*
숨겨지지 않은 파일만 확장되므로 디렉터리가 비어 있는지 테스트하는 대신 디렉터리에 숨겨지지 않은 파일이 포함되어 있는지 테스트합니다. 일부 셸의 경우dotglob
또는 옵션을 사용 하거나 특수 변수(셸에 따라)를globdot
사용하여 문제를 해결할 수 있습니다.FIGNORE
[ -e "$1" ]
stat()
시스템 호출이 성공했는지 테스트합니다 . 첫 번째 파일이 액세스할 수 없는 위치에 대한 심볼릭 링크인 경우 false가 반환됩니다. 디렉토리가 비어 있는지 확인하기 위해 파일이 필요하지 않습니다stat()
(또는 필요하지도 않음lstat()
). 내용이 있는지 확인하기만 하면 됩니다.*
확장 프로그램은 현재 디렉터리를 열고, 모든 항목을 검색하고, 숨겨지지 않은 모든 항목을 저장하고 정렬해야 하는데, 이는 역시 매우 비효율적입니다.
디렉토리가 비어 있지 않은지( 및를 제외한 모든 항목이 있는지) 확인하는 가장 효율적인 방법 은 glob 한정자를 사용하는 .
것입니다 ( for..
zsh
F
F
가득한, 여기서는 비어 있지 않음을 의미합니다):
if [ .(NF) ]; then
print . is not empty
else
print "it's empty or I can't read it"
fi
N
nullglob
글로벌 예선 입니다 . 따라서 가득 차면 .(NF)
확장되고 , 그렇지 않으면 아무것도 없습니다..
.
lstat()
디렉토리에 있는 후 zsh
링크 수가 2보다 큰 것으로 확인되면 하위 디렉토리가 하나 이상 있으므로 비어 있지 않으므로 디렉토리를 열 필요조차 없습니다. 이 경우 읽기 권한이 없어도 디렉토리가 비어 있지 않음을 알 수 있습니다. 그렇지 않으면 zsh는 디렉토리를 열고 해당 내용을 읽은 후 모든 내용을 읽거나 저장하거나 정렬 .
하지 않고 둘 중 하나도 아닌 첫 번째 항목에서 중지합니다...
POSIX 쉘( zsh
에뮬레이션에서 POSIX적으로만 작동 sh
)을 사용하면 glob을 사용하여 디렉토리가 비어 있지 않은지 확인하는 것은 매우 어색합니다.
한 가지 방법은 다음과 같습니다.
set .[!.]* '.[!.]'[*] .[.]?* [*] *
if [ "$#$1$2$3$4$5" = '5.[!.]*.[!.][*].[.]?*[*]*' ]; then
echo "empty or can't read"
else
echo not empty
fi
(Glob 관련 옵션이 기본값에서 변경되지 않고(POSIX만 지정 ), (for ) 및 (for) 변수가 noglob
설정되지 않고 (for) 유효한 문자를 형성하지 않는 바이트 시퀀스가 포함된 파일 이름이 없다고 가정합니다. ).GLOBIGNORE
bash
FIGNORE
ksh
yash
POSIX 셸에서 glob이 일치하지 않으면 확장되지 않는다는 아이디어입니다(이것은 1970년대 후반 Bourne 셸에 도입된 버그 기능이었습니다). 따라서 ==를 set -- *
얻으면 일치하는 항목이 없기 때문인지, 아니면 이름이 존재하는 파일이 있는지 알 수 없습니다 .$1
*
*
문제를 해결하는 (결함 있는) 방법은 을 사용하는 것입니다 [ -e "$1" ]
. set -- [*] *
파일이 없으면 위의 파일이 남아 있고 숨겨진 파일 [*] *
에 대해 유사한 작업을 수행하기 *
때문에 두 경우를 명확하게 합니다. 이는 Bourne 쉘의 또 다른 결함( Forsyth 쉘, pdksh 및 pdksh에 의해 수정됨 ) * *
으로 인해 다소 당혹스럽습니다. 확장에는 특수(의사) 항목이 포함되어 있으며 보고 될 때 .zsh
fish
.*
.
..
readdir()
따라서 모든 쉘에서 작동하게 하려면 다음을 수행할 수 있습니다.
cwd_empty()
if [ -n "$ZSH_VERSION" ]; then
eval '! [ .(NF) ]'
else
set .[!.]* '.[!.]'[*] .[.]?* [*] *
[ "$#$1$2$3$4$5" = '5.[!.]*.[!.][*].[.]?*[*]*' ]
fi
그럼에도 불구하고, zsh
기본 구문은 POSIX sh 구문과 호환되지 않습니다. 이는 Bourne 셸(POSIX.2가 처음 출시되기 오래 전)의 대부분의 주요 문제를 이전 버전과 호환되지 않는 방식으로 수정하기 때문입니다 *
. Bourne 셸 이전의 셸에는 이 문제가 없었 csh
으며 tcsh
그렇지 fish
않았습니다. .*
포함하고 있지만 .
분할 +glob과 같은 여러 다른 항목은 인수 또는 산술 확장에 대해 수행하므로 에뮬레이션을 열지 않는 한 ..
POSIX sh로 작성된 코드를 기대할 수 없습니다. 항상 작동합니다.zsh
sh
시뮬레이션 sh
은 특별히 존재하므로 에서 할 수 있습니다 zsh
.
POSIX에서 파일에 쓰려면 source
다음과 같이 하면 됩니다.sh
zsh
emulate sh -c 'source ./that-file'
그런 다음 파일은 시뮬레이션에서 평가되며 sh
파일에 선언된 모든 기능은 해당 시뮬레이션 모드로 유지됩니다.
답변2
zsh의 기본 동작은 오류를 발생시키는 것이지만,이는 nomatch
옵션에 의해 제어 됩니다.. 이 옵션을 설정 해제 *
하여 bash 및 dash와 같이 그대로 둘 수 있습니다 .
setopt -o nonomatch
이 명령은 다른 명령에는 영향을 미치지 않지만 무시할 수 있습니다.
setopt -o nonomatch 2>/dev/null || true ; set *
이는 zsh에서 실행되며 다른 명령에서 실패한 명령에 대한 setopt
오류 출력( ) 및 반환 코드( )를 억제합니다.2>/dev/null
|| true
작성된 대로 다음과 같은 파일이 있는 경우 명령이 실패하면 종료하도록 셸 옵션을 -e
실행 set -e
하고 변경합니다. 창의력을 발휘하면 결과가 더 나빠질 것입니다. set -- *
더욱 안전해지고 옵션 변경을 방지할 수 있습니다.
답변3
Zsh의 구문은 sh와 호환되지 않습니다. sh처럼 보일 만큼 가깝지만 sh 코드를 수정하지 않고 실행할 수 있을 만큼 가깝지는 않습니다.
예를 들어 sh용으로 작성된 sh 함수 또는 코드 조각이 있고 이를 zsh 스크립트에서 사용하려는 경우 zsh에서 sh 코드를 실행하려는 경우 다음을 사용할 수 있습니다.emulate
내장. 예를 들어, zsh 스크립트에서 sh에 대해 작성된 파일을 얻으려면 다음을 수행하십시오.
emulate sh -c 'source /path/to/file.sh'
sh 구문을 사용하여 함수나 스크립트를 작성하고 zsh에서 실행하려면 시작 부분에 배치하세요.
emulate -L sh 2>/dev/null || true
sh 구문에서 zsh는 모든 POSIX 구성을 지원합니다( bash --posix
ksh93 또는 mksh와 마찬가지로 POSIX와 호환됩니다). 또한 배열(ksh, bash 및 ksh에서는 0-인덱싱 emulate sh
되지만 기본 zsh에서는 1-인덱싱됨) 및 와 같은 일부 ksh 및 bash 확장을 지원합니다 [[ … ]]
. emulate … ksh …
bash emulate … sh …
(이것은 스크립트/함수에 고유한 것이 아닙니다).if [[ -n $BASH ]]; then shopt -s extglob; fi
.
및를 제외하고 디렉토리의 모든 항목을 열거하는 기본 zsh 방법 ..
은 다음과 같습니다.
set -- *(DN)
이는 다음을 사용합니다.글로벌 예선 D
도트 파일을 포함하고 N
일치하는 항목이 없으면 빈 목록을 생성합니다.
.
및를 제외하고 디렉토리의 모든 항목을 열거하는 이식 가능한 sh 방법은 ..
훨씬 더 복잡합니다. 도트 파일을 나열해야 하며, 현재 디렉터리의 파일을 나열하거나 절대 경로가 보장되지 않는 경우 파일 이름이 대시로 시작하는 경우 주의해야 합니다. 확장되지 않은 패턴을 ..?* .[!.]* *
제외하고 패턴이 있는 모든 파일을 나열하고 .
제거 하는 방법은 다음과 같습니다 ..
.
set -- ..?*
if [ "$#" -eq 1 ] && ! [ -e "$1" ] && ! [ -L "$1" ]; then shift; fi
set -- .[!.]* "$@"
if [ "$#" -eq 1 ] && ! [ -e "$1" ] && ! [ -L "$1" ]; then shift; fi
set -- * "$@"
if [ "$#" -eq 1 ] && ! [ -e "$1" ] && ! [ -L "$1" ]; then shift; fi
디렉토리가 비어 있는지 테스트하고 싶다면 더 쉬운 방법이 있습니다.
if ls -A /path/to/directory/ | grep -q '^'; then
echo "/path/to/directory is not empty"
else
echo "/path/to/directory is empty"
fi
답변4
해결책이 무엇인지 잘 모르겠습니다 set *
. 이건 어때?
dir="./empty"
has_file=false
for i in "$dir"/*; do
if test ! "$i" = "$dir/*"; then
has_file=true
echo "$i"
if
done
echo "$has_file"
테스트를 거쳤으며 이는 zsh
, bash
, ash
, ksh
, AKA에 bourne
적합합니다.heirloom