일반적으로 스크립트를 편집하면 해당 스크립트를 실행 중인 모든 용도에서 오류가 발생하기 쉽습니다.
예:
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_sourced
BASH_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는 백업용 파일을 불필요하게 체크섬하지 않으며 하드 링크 기능이 예상대로 작동합니다.
편집하는 동안 파일을 바꾸는 것은 작동하지만 다른 스크립트/사용자에 대해 실행할 수 없거나 사람들이 잊어버릴 수 있으므로 덜 강력합니다. 하드 링크가 다시 끊어집니다.