실행하기 전에 전체 쉘 스크립트를 읽는 방법은 무엇입니까?

실행하기 전에 전체 쉘 스크립트를 읽는 방법은 무엇입니까?

일반적으로 스크립트를 편집하면 해당 스크립트를 실행 중인 모든 용도에서 오류가 발생하기 쉽습니다.

예:

sleep 20

echo test

이 스크립트를 실행하면 bash는 첫 번째 줄(예: 10바이트)을 읽고 절전 모드로 전환됩니다. 복구되면 바이트 10부터 시작하는 스크립트의 내용이 다를 수 있습니다. 완전히 다른 분기의 다른 줄 중간에서 실행이 시작될 수도 있습니다 if. 실행 중인 스크립트가 삭제됩니다.

그렇다면 나중에 편집해도 실행 중인 인스턴스에 영향을 주지 않도록 실행하기 전에 전체 셸 스크립트를 어떻게 읽을 수 있습니까?

답변1

예, 쉘은 bash파일을 한 번에 한 줄씩 읽는 데 특별한 주의를 기울입니다. 따라서 대화식으로 사용할 때와 동일한 방식으로 작동합니다.

파일을 검색할 수 없는 경우(예: 파이프), bash한 번에 한 바이트씩 읽어서 해당 \n문자만 읽게 된다는 것을 알 수 있습니다. 파일을 검색할 수 있으면 전체 블록을 한 번에 읽어 최적화하지만 \n.

이는 다음을 수행할 수 있음을 의미합니다.

bash << \EOF
read var
var's content
echo "$var"
EOF

아니면 스스로 업데이트되는 스크립트를 작성하세요. 그 보장을 제공하지 않으면 그렇게 할 수 없습니다.

이제 이와 같은 작업을 수행하고 싶지 않을 것이며, 이 기능이 유용하기보다는 방해가 되는 경우가 더 많다는 것을 알게 될 것입니다.

이를 방지하려면 파일을 제자리에서 수정하지 않도록 노력할 수 있습니다(예: 복사본을 수정한 다음 복사본을 제자리로 이동(예: sed -i일부 perl -pi편집자가 수행하는 작업)).

또는 다음과 같이 스크립트를 작성할 수 있습니다.

{
  sleep 20
  echo test
}; exit

( 닫는 괄호 바로 앞 중괄호 안에 넣을 수도 있지만 exit; 와 같은 줄에 있는 것이 중요합니다 .)}

또는:

main() {
  sleep 20
  echo test
}
main "$@"; exit

작업 수행을 시작하기 전에 쉘은 스크립트를 읽어야 합니다 exit. 이렇게 하면 쉘이 스크립트에서 다시 읽지 않게 됩니다.

이는 전체 스크립트가 메모리에 저장된다는 의미입니다.

이는 스크립트 구문 분석에도 영향을 미칩니다.

예를 들면 다음과 같습니다 bash.

export LC_ALL=fr_FR.UTF-8
echo $'St\ue9phane'

UTF-8로 인코딩된 U+00E9를 출력합니다. 그러나 다음과 같이 변경하면:

{
  export LC_ALL=fr_FR.UTF-8
  echo $'St\ue9phane'
}

\ue9명령을 구문 분석할 때 적용되었던 문자 세트로 확장됩니다. 이 경우앞으로명령이 export실행됩니다.

source또한 aka 명령을 사용하는 경우 .일부 쉘에서는 소스 파일과 동일한 문제가 발생합니다.

bash그러나 해당 명령은 파일을 해석하기 전에 source파일을 완전히 읽기 때문에 그렇지 않습니다 . 구체적으로 작성된 경우 bash스크립트 시작 부분에 다음을 추가하여 실제로 이를 활용할 수 있습니다.

if [[ ! $already_sourced ]]; then
  already_sourced=1
  source "$0"; exit
fi

(나는 이것에 의존하지 않을 것입니다. 미래 버전에서 이 동작이 변경될 수 있다고 상상할 수 있지만 이는 현재 제한 사항 bash으로 간주될 수 있습니다(bash 및 AT&T ksh는 우리가 아는 한 POSIX처럼 동작하는 유일한 셸입니다). already_sourcedBASH_SOURCE 변수의 내용에 영향을 미칠 것이라는 점은 말할 것도 없고 변수가 환경에 없다고 가정하기 때문에 약간 취약합니다.)

답변2

파일을 삭제하기만 하면 됩니다(예: 파일을 복사하고, 삭제하고, 복사본의 이름을 원래 이름으로 다시 변경). 실제로 많은 편집자가 이 작업을 수행하도록 구성할 수 있습니다. 파일을 편집하고 변경된 버퍼를 파일에 저장하면 파일을 덮어쓰지 않고 이전 파일의 이름을 바꾸고 새 파일을 만든 다음 새 내용을 새 파일에 넣습니다. 따라서 실행 중인 모든 스크립트는 문제 없이 계속되어야 합니다.

RCS(vim 및 emacs용)와 같은 간단한 버전 제어 시스템을 사용하면 변경 내역을 보유하는 두 가지 이점을 얻을 수 있으며 체크아웃 시스템은 기본적으로 현재 파일을 삭제하고 올바른 스키마로 다시 생성해야 합니다. (물론 그러한 파일을 하드 링크하는 데 주의하십시오).

답변3

사용:

{
  ... your code ...

  exit
}

{}Bash는 실행하기 전에 전체 블록을 읽으며, 이 exit명령은 코드 블록 외부의 어떤 것도 읽히지 않도록 보장합니다.

실행하는 대신 "가져오는" 스크립트의 경우 다음을 사용하세요.

{
  ... your code ...

  return 2>/dev/null || exit
}

답변4

스크립트를 블록으로 래핑하는 것이 {}가장 좋은 옵션일 수 있지만 스크립트를 변경해야 합니다.

F=$(mktemp) && cp test.sh $F && bash $F; rm $F;

두 번째로 좋은 선택이 될 것입니다(가정임시 파일 시스템) 단점은 스크립트에서 $0을 사용하면 $0이 삭제된다는 것입니다.

이와 같은 것을 사용하는 것은 F=test.sh; tail -n $(cat "$F" | wc -l) "$F" | bash전체 파일을 메모리에 유지하고 $0을 파괴해야 하기 때문에 이상적이지 않습니다.

마지막 수정 시간, 읽기 잠금 및 하드 링크가 방해받지 않도록 원본 파일에 대한 액세스는 피해야 합니다. 이렇게 하면 파일을 실행하는 동안 편집기를 열어 둘 수 있고 rsync는 백업용 파일을 불필요하게 체크섬하지 않으며 하드 링크 기능이 예상대로 작동합니다.

편집하는 동안 파일을 바꾸는 것은 작동하지만 다른 스크립트/사용자에 대해 실행할 수 없거나 사람들이 잊어버릴 수 있으므로 덜 강력합니다. 하드 링크가 다시 끊어집니다.

관련 정보