파일을 소싱하기 전에 파일이 있는지 확인하는 이유는 무엇입니까?


13

파일을 소스하려고 할 때 파일이 존재하지 않는다는 오류가 표시되지 않도록 수정해야 할 사항이 있습니까?

예를 들어, nvm 은 이것을 프로파일 / rc에 추가 할 것을 권장합니다.

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

위와 같이 nvm.sh존재하지 않으면 "자동 오류"가 발생합니다. 그러나 시도 . "$NVM_DIR/nvm.sh"하면 출력이됩니다 FILE_PATH: No such file or directory.


3
해서는 안됩니다. 성가시다. 소스를 시도하고 오류가있는 경우 오류를 처리하십시오.
Mikel

2
@ 미켈 맞아! 그럼 왜 어디서나 볼 수 있습니까?
JBallin

3
@FaheemMitha, 당신이하고있는 일이 보안에 민감하다면 (즉, 귀하의 프로그램이 다른 사람을 대신하여 작동하는 경우), 경쟁 조건 ( TOCTOU )에 대해 정말로 신경 써야 합니다. 그러나 누군가가에서 파일을 수정하면 더 큰 문제가 있기 때문에 아마도 여기에 해당되지 않습니다 HOME.
ilkkachu

8
@FaheemMitha 존재를 먼저 확인하는 것이 낫지 않습니다 . 테스트와 사용간에 상황이 변경되어 오탐 (false positive)과 오탐 (false negative)이 모두 발생할 수 있습니다. 그래도 사용 실패를 처리해야합니다. 성능에 대해 언급했듯이 사용하기 전에 테스트하는 속도는 그렇지 않은 경우보다 두 배 느리게 진행됩니다. 할 수 없습니다.
user207421

1
@SimonRichter,이 경우 nvm이 작동하지 않습니다. 사용자는 파일이 스스로 없다는 것을 알아 내야합니다.
JBallin

답변:


25

POSIX 셸에서 .특수 내장 기능이므로 셸이 실패하면 셸이 종료됩니다 (와 같은 일부 셸 bash에서는 POSIX 모드에서만 수행됨).

오류로 규정되는 것은 쉘에 따라 다릅니다. 파일을 구문 분석 할 때 구문 오류로 인해 모두 종료되는 것은 아니지만 소스 파일을 찾거나 열 수 없으면 대부분 종료됩니다. 소스 파일의 마지막 명령이 0이 아닌 종료 상태로 반환 된 경우 종료되는 errexit옵션을 알 수 없습니다 ( 옵션이 물론 그렇지 않은 경우 ).

여기에 :

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

파일이있는 경우 소스를 원하고 그렇지 않은 경우 (또는 여기에 비어있는 경우)하지 마십시오 -s.

즉, 파일이 없으면 오류 (POSIX 셸에서 치명적인 오류)로 간주되어서는 안되며 해당 파일은 선택적 파일로 간주됩니다.

파일을 읽을 수 없거나 디렉토리 인 경우 (치명적) 오류이거나 구문 분석 중에 구문 오류가있는 경우보고해야하는 실제 오류 조건 일 수 있습니다 (치명적).

경쟁 조건이 있다고 주장하는 사람들도 있습니다. 그러나 그 의미 유일한 것은 파일이 사이에서 제거되는 경우 쉘이 오류로 종료 것이 될 것이다 [그리고 .,하지만 난 그게 스크립트가있는 동안이 고정 경로 파일이 사라지고 갑자기 것이라고하면 오류를 고려하는 것이 유효의 주장 것 달리는.

반면에

command . "$NVM_DIR/nvm.sh" 2> /dev/null

여기서 command¹는 명령에 대한 특수 속성을 제거 .하므로 (오류시 쉘을 종료하지 않음) 다음과 같이 작동하지 않습니다.

  • .의 오류를 숨길 뿐만 아니라 소스 파일에서 실행되는 명령의 오류도
  • 또한 잘못된 권한을 가진 파일과 같은 실제 오류 조건을 숨길 수 있습니다.

다른 일반적인 구문 (예를 들어 grep -r /etc/default /etc/init*, systemd아직 변환되지 않은 init 스크립트에 대해서는 데비안 시스템 참조) ( EnvironmentFile=-/etc/default/service옵션 환경 파일을 대신 지정하는 데 사용) :

  • [ -e "$file" ] && . "$file"

    파일이 있는지 확인하고 비어 있으면 소스를 지정하십시오. 열 수없는 경우에도 여전히 치명적인 오류가 있습니다 (있는 경우 또는 거기에 있더라도). [ -f "$file" ](존재하고 일반 파일 임), [ -r "$file" ](읽기 가능) 또는 이들의 조합 과 같은 더 많은 변형을 볼 수 있습니다 .

  • [ ! -e "$file" ] || . "$file"

    약간 더 나은 버전. 존재하지 않는 파일이 정상임을 분명히합니다. 이는 또한 $?마지막에 실행 된 명령의 종료 상태를 반영 한다는 것을 의미합니다 $file(이전의 경우, 이 명령이 존재하지 않았는지 또는 해당 명령이 실패 1했는지 여부를 알 수 없음 $file).

  • command . "$file"

    파일이있을 것으로 예상하지만 해석 할 수 없으면 종료하지 마십시오.

  • [ ! -e "$file" ] || command . "$file"

    위의 조합 : 파일이 없으면 OK이며 POSIX 쉘의 경우 파일을 열거 나 구문 분석하는 데 실패했지만 치명적이지는 않습니다 (더 바람직합니다 ~/.profile).


¹ 참고 : zsh그러나 에뮬레이션 command이 없으면 그렇게 사용할 수 없습니다 sh. Korn 쉘에서 source실제로는 command .특수한 변형이 아닌에 대한 별칭 입니다..


흥미 롭습니다! POSIX sh에 대해서는 몰랐습니다. 그러나 문제는에 관한 것이었다 .bash_profile. 죄송합니다 .bash_profile.
Mikel

(질문의 nvm 소스 링크를 읽은 것에 기초하여이 질문을 모든 POSIX 쉘에보다 광범위하게 적용하는 것으로 해석 할 수 있음을 알고 있습니다.)
Mikel

@Mikel, bashPOSIX 모드가 아닌 경우 에도 여전히 대답이 적용됩니다 . [ -e /file ] && . /file파일이 존재하지 않을 때 오류로 간주하지 않으 려면 원합니다 . 여기서 시도 할 수없는 경우 try 소스는 오류를 처리합니다 .
Stéphane Chazelas

1
@Mikel, 그것은 비생산적입니다. 1) POSIX 쉘 (또는 POSIX 모드의 bash)에 오류가 발생하면 종료를 막지 않습니다 .2) 오류 메시지 (stdout에 있음)를 복제하는 오류 .는 이미 stderr에 오류를보고합니다. 그리고 파일이 존재하지 않을 때 오류로 간주하지 않으려는 경우에는 정확하지 않습니다 (그리고 .파일이 존재하지 않았거나 읽을 수 없었기 때문에 종료 상태에서 실패 했는지 여부를 알 수 없음) 파싱 ​​할 수 없거나 마지막 명령이 실패했습니다)
Stéphane Chazelas

1
경쟁 조건과 관련하여- 로그인이 일관성있게 실패하는 것보다 (시스템에서 이상한 일이 발생하는 동안) 번의 로그인 실패 (시스템에서 이상한 일이 발생하는 경우)의 IMHO 문제는 훨씬 적습니다 (사용자 구성에 옳지 않은 것이 있더라도) -파일). 따라서이 검사에서 경쟁 조건은 여전히 ​​검사를하지 않는 것보다 개선 된 것입니다.
ruakh

5

의 유지 관리자 nvm의 응답 :

단순히 파일을 삭제하여 nvm을 쉽게 제거 할 수 있습니다. 추가 작업을 강요하는 것은 (라인 ns가 소스 nvm 인 위치를 추적하기 위해) 특별히 가치가없는 것으로 보입니다.

내 해석 (Stéphane의 훌륭한 설명 및 Kusalananda의 의견과 결합 됨) :

더 간단하고 안전합니다.

파일 누락으로 인해 시작시 POSIX 셸이 종료되는 것을 방지합니다 (여러 가지 이유로). 비 POSIX (예 : bash) 쉘을 사용하는 사용자는 원하는 경우 조건부를 제거 할 수 있습니다.


1
"초보자를 겨냥한"해석에 반대합니다. 방어 적입니다. 어떤 이유로 파일이 없어져서 시작시 사용자의 로그인 쉘이 예기치 않게 종료되는 것을 원하지 않습니다 . 해당 코드 줄이의 쉘 초기화 파일 중 하나에 /etc있으면 일부 사용자는 파일을 가지고 있고 일부는 파일을 가지고 있지 않습니다. IMHO, nvm관리자의 반응은 한 가지 측면에만 영향을 미칩니다.
Kusalananda

1

으로 JBallin스테판 Chazelas가 실패에 로그인하는 원인이 존재하지 않는 파일을 소싱, POSIX 쉘에서 지적했다.

그러나 파일이 존재하는지 확인한 다음 소스를 찾으려면 테스트를 추가하면 경쟁 조건이 발생할 수 있습니다. 와 nvm.sh사이에 무언가가 변경되면 훨씬 더 드물지만 예방하려는 버그가 발생합니다.[ -s nvm.sh ]. nvm.sh

일반적으로 경쟁 조건을 방지하는 방법은 수행하려는 작업을 시도한 다음 실패하면 오류를 처리하는 것입니다.

. "$NVM_DIR/nvm.sh" || echo "Sourcing $NVM_DIR/nvm.sh failed" >&2

위와 같이 .실패하면 오류 처리가 실행되기 전에 쉘이 즉시 종료 되므로 POSIX 쉘에서는 이것이 작동하지 않는 것으로 나타났습니다 .

내 대답은 POSIX 쉘이 .bash_profilePOSIX 모드에서 실행되어서는 안되기 때문에이 질문과 관련이 없다고 주장 합니다. 어쨌든 위의 코드를 수행 할 수 있습니다.

/unix//a/383581/3169에 설명 된 기술을 사용하여 POSIX 모드가 적용되지 않도록하거나 POSIX 모드를 비활성화 할 수 있습니다 .

Stéphane의 답변에는 모든 POSIX 셸을 처리하는 방법에 대한 유용한 제안이 있습니다 .NVM 작성자의 의도라고 생각하지만 여기에서 묻는 질문과 미묘하게 다릅니다. .

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