여러 모드를 사용하여 "ls"를 실행할 때 0이 아닌 종료 코드를 사용하지 마세요.

여러 모드를 사용하여 "ls"를 실행할 때 0이 아닌 종료 코드를 사용하지 마세요.

두 가지 가능한 경로가 있고 Linux 시스템의 디렉터리와 파일을 나열하고 싶다고 가정해 보겠습니다.

/some/path1/
/some/path2/

다음을 수행하면 tcsh종료 코드 중 하나 이상이 존재하는 경우 종료 코드를 얻습니다.0path1path2

ls -d /some/{path1,path2}/*

그러나 에서 똑같은 작업을 수행하면 종료 코드와 해당 코드가 존재하지 않는다는 메시지가 표시 bash됩니다 (path1이 존재하지 않는 경로인 경우).2stderrpath1

이 상황에서 어떻게 bash이렇게 행동할 수 있습니까? tcsh하나 이상의 경로가 존재하는 경우 반환하도록 요청할 수 있는 스위치가 있습니까 ? 둘 다 없으면 0이 아닌 코드가 반환될 것으로 예상합니다.ls0tcsh

답변1

귀하의 질문에 대한 대부분의 답변은 다음 주소에서 해결되었습니다.nullglob이 기본값이 아닌 이유는 무엇입니까?.

기억해야 할 한 가지는 다음과 같습니다.

ls -d /some/{path1,path2}/*

csh// tcsh/ zsh/ (그러나 아래는 아님 ) 다음을 사용 bash하여:kshfish

ls -d /some/path1/* /some/path2/*

지원 확대를 수행하는 경우앞으로(아니요의 일부로/some/pathx/*) glob 확장. 이는 에 별도의 인수로 전달될 일치하는 파일 목록으로 이러한 패턴을 셸에서 확장하는 것입니다 ls.

bash, ksh주로 복사하는 것은 Bourne 쉘에 의해 도입된 버그 기능을 상속합니다. 따라서 glob 패턴이 어떤 파일과도 일치하지 않을 때 명령에 대한 인수로 효과적으로 전달됩니다.

따라서 이러한 셸에서 /some/path1/*하나 이상의 파일이 일치하고 /some/path2/*일치하는 파일이 없으면 , 리터럴을 인수로 사용하여 호출됩니다 ls. 파일이 존재하지 않기 때문에 오류가 보고됩니다.-d/some/path1/file1/some/path1/file2/some/path2/*/some/path2/*ls

csh이 동작은 glob이 쉘이 아니라 /etc/glob도우미 유틸리티(유틸리티에 glob이라는 이름을 부여함)에 의해 실행되는 이전 버전의 Unix와 유사합니다. 도우미는 명령을 호출하기 전에 전역 확장을 수행하고 다음 No match과 같은 경우 명령을 실행하지 않고 오류를 보고합니다.모두glob 패턴은 어떤 파일과도 일치할 수 없습니다. 그렇지 않으면 일치하는 글로브가 하나 이상 있는 한 일치하지 않는 모든 글로브가 제거됩니다.

따라서 위의 예에서는 csh/ tcsh또는 오래된 Thompson 쉘과 그 /etc/glob도우미를 사용하여 호출 만 ls수행 되고 성공할 가능성이 가장 높습니다 (검색 가능한 한 ).-d/some/path1/file1/some/path1/file2/some/path1

zshKorn과 유사한 쉘이자 csh와 유사한 쉘입니다. 일치하지 않는 glob이 있는 그대로 전달된다는 점에서 Bourne 쉘의 결함이 발생하지 않지만 기본값은 csh이보다 더 엄격하고 실패한 모든 glob은 치명적인 오류로 처리됩니다. 따라서 zsh기본적으로 하나 /some/path1/*또는 /some/path2/*둘 다 일치할 수 없으면 명령이 중단됩니다. option² 을 사용하여 셸에서 bash유사한 동작을 활성화 할 수 있습니다 failglob.

이는 동작을 더 예측 가능하고 일관되게 만들지만 여러 glob 확장을 명령에 전달하고 glob 중 하나가 성공할 때마다 실패하는 것을 원하지 않을 때 문제가 발생할 수 있음을 의미합니다. 그러나 cshnullglobcsh와 유사한 동작(또는 emulate csh)을 얻기 위한 옵션을 설정할 수 있습니다.

이는 익명 함수를 사용하여 로컬에서 수행할 수 있습니다.

() { set -o localoptions -o cshnullglob; ls -d /some/{path1,path2}/*; }

아니면 그냥 서브쉘을 사용하세요:

(set -o cshnullglob; ls -d /some/{path1,path2}/*)

그러나 여기서는 두 개의 glob을 사용하는 대신 교대 glob 연산자를 사용하여 모든 glob과 일치하는 하나의 glob을 사용할 수 있습니다.

ls -d /some/(path1|path2)/*

여기서도 할 수 있습니다:

ls -d /some/path[12]/*

에서는 대체를 포함하여 ksh의 확장된 glob 연산자의 하위 집합을 지원하도록 bash에 대한 옵션을 bash활성화할 수 있습니다 .extglob

(shopt -s extglob; ls -d /some/@(path1|path2)/*)

이제 Bourne 셸에서 상속된 버그 기능으로 인해 glob이 어떤 파일과도 일치하지 않으면 /some/@(path1|path2)/*그대로 전달되고 ls결국 lsliteral이라는 파일이 나열될 수 있으므로 다음 을 방지하려면 해당 옵션 /some/@(path1|path2)/*도 활성화해야 합니다 .failglob

(shopt -s extglob failglob; ls -d /some/@(path1|path2)/*)

nullglob또는 옵션( bash에서 복사됨 zsh)을 사용할 수 있습니다.모두일치하지 않는 글로브는 비어 있게 확장됩니다. 하지만:

(shopt -s nullglob; ls -d /some/path1/* /some/path2/*)

명령의 특별한 경우에는 ls인수 목록이 전달되지 않으면 오류가 발생합니다 .. 그러나 nullglobglob 확장을 사용하여 배열에 저장하고 ls배열 멤버가 비어 있지 않은 경우에만 매개변수로 호출할 수 있습니다.

(
  shopt -s nullglob
  files=( /some/path1/* /some/path2/* )
  if (( ${#files[@]} > 0 )); then
    ls -d -- "${files[@]}"
  else
    echo >&2 No match
    exit 2
  fi
)

전역적으로 활성화하는 대신 zshglob 한정자(ksh에 영감을 주었지만 아직 복사되지 않음)를 사용하여 nullglobglob별로 활성화하고 다시 배열 대신 익명 함수를 사용할 수 있습니다.(N)~(N)bash

() {
  (( $# > 0 )) && ls -d -- "$@"
} /some/path1/*(N) /some/path2/*(N)

이제 쉘은 glob이 (배열 할당을 위해) 사용되거나 어떤 방식으로 동작 하지 않는 한 오류를 일으키는 실패한 glob fish처럼 동작합니다 .zshforsetcountnullglob

또한 에서 fish중괄호 확장 자체는 와일드카드가 아니지만 와일드카드의 일부로 수행됩니다. 또는 중괄호 확장이 와일드카드와 결합되면 최소한 명령을 중단하지 않고 하나 이상의 요소를 반환할 수 있습니다.

따라서 fish:

ls -d /some/{path1,path2}/*

결국 처럼 행동할 것입니다 csh.

심지어:

{ls,-d,/xx*}

불일치 ls로 인해 실패가 아닌 별도의 호출이 발생합니다(이 경우 동작은 csh와 다릅니다).-d/xx*

어쨌든 파일 경로만 일치시키는 경우에는 print필요하지 않습니다 ls. 에서는 내장 기능을 사용하여 열별로 인쇄 zsh할 수 있습니다 .print

print -rC3 /some/{path1,path2}/*(N)

aw 경로는 3개 열에 인쇄됩니다 r(ullglob glob 한정자와 일치하지 않으면 아무것도 인쇄되지 않습니다 N).

대신 이 두 디렉터리 중 하나에 숨겨지지 않은 파일이 하나 이상 있는지 확인하려면 다음을 수행할 수 있습니다.

# bash
if (shopt -s nullglob; set -- /some/path1/* /some/path2/*; (($#))); then
   echo yes
else
   echo no
fi

또는 다음 기능을 사용하세요.

has_non_hidden_files() (
  shopt -s nullglob
  set -- "$1"/*
  (($#))
)
if has_non_hidden_files /some/path1 || has_non_hidden_files /some/path2
then
  echo yes
else
  echo no
fi
# zsh
if ()(($#)) /some/path1/*(N) /some/path2/*(N); then
  echo yes
else
  echo no
fi

또는 다음 기능을 사용하세요.

has_non_hidden_files() ()(($#)) $1/*(NY1)
if has_non_hidden_files /some/path1 || has_non_hidden_files /some/path2
then
  echo yes
else
  echo no
fi

( Y1첫 번째 파일을 찾은 후 중지되는 최적화로)

has_non_hidden_files사용자가 읽을 수 없는 디렉터리(파일이 있는지 여부에 관계없이)에 대해서는 (자동) false를 반환합니다. 에서는 zsh특수 변수를 통해 이러한 상황을 감지할 수 있습니다.$ERRNO


1 Bourne 동작(POSIX에서 지정) 은 다음을 실행 zsh하여 활성화할 수 있습니다.emulate shemulate kshset +o nomatch

² 전역 불일치가 있을 때 정확히 어떤 동작이 취소되는지에는 상당한 차이가 있습니다. 어떤 fish동작이 더 합리적이고 bash -O failglob최악일 수도 있습니다.

답변2

그냥 읽어봐스티븐의 대답.


bash이는 아무것도 일치하지 않는 글로브를 처리하는 방법 때문에 발생합니다 . 에서 관찰한 것과 동일한 동작을 얻으려면 tcshbash에 대한 옵션을 활성화해야 합니다 nulglob. 에서 man bash:

빈 공

설정된 경우, bash는 어떤 파일과도 일치하지 않는 패턴(위의 경로 이름 확장 참조)이 자신이 아닌 빈 문자열로 확장되도록 허용합니다.

활성화 nullglob하면 shopt -s nullglob예상되는 동작을 얻을 수 있습니다.

$ ls
bar
$ ls {bar,foo}/*
ls: cannot access 'foo/*': No such file or directory
 bar/ff
$ echo $?
2


$ shopt -s nullglob 
$ ls {bar,foo}/*
bar/ff
$ echo $?
0

관련 정보