
"패치" 스크립트를 실행하는 보드가 있습니다. 패치 스크립트는 항상 백그라운드에서 실행되며 다음 의사 코드를 실행하는 셸 스크립트입니다.
while true; do
# checks if a patch tar file exists and if yes then do patching
sleep 10
done
이 스크립트는 /opt/patch.sh에 있으며 SystemV init 스크립트에 의해 시작되었습니다.
문제는 스크립트가 tar를 찾으면 이를 추출하고, 내부에는 tar라는 셸 스크립트가 있다는 것입니다.patch.sh이는 tar의 내용에 따라 다릅니다.
스크립트가/opt/patch.sh다음을 수행하는 tar를 찾습니다.
tar -xf /opt/update.tar -C /mnt/update
mv /mnt/update/patch.sh /opt/patch.sh
exec /opt/patch.sh
자신을 다른 스크립트로 대체하고 동일한 위치에서 실행합니다. 그렇게 하면 어떤 문제가 발생할 수 있나요?
답변1
파일이 제자리에 덮어쓰기되어 교체되는 경우(inode는 동일하게 유지됨) 해당 파일을 연 모든 프로세스는 파일에서 읽을 때 새 데이터를 보게 됩니다. 이전 파일의 링크를 해제하고 동일한 이름의 새 파일을 생성하여 대체하는 경우 inode 번호가 변경되고 파일을 열어 둔 모든 프로세스는 여전히오래된파일.
mv
파일 시스템 간에 이동이 발생하는지 여부에 따라 둘 중 하나를 수행할 수 있습니다. 완전히 새로운 파일을 얻으려면 먼저 원본 파일의 링크를 해제하거나 이름을 바꾸십시오. 이 같은:
mv /opt/patch.sh /opt/patch.sh.old # or rm
mv /mnt/update/patch.sh /opt/patch.sh
이렇게 하면 실행 중인 셸은 이동 후에도 이전 데이터에 대한 파일 핸들을 계속 갖게 됩니다.
즉, 내가 테스트한 한 Bash는 루프를 실행하기 전에 전체 루프를 읽으므로 기본 파일에 대한 변경 사항은 실행이 루프 내에 유지되는 한 실행 중인 스크립트를 변경하지 않습니다. (끝에서 전체 루프에 영향을 미치는 리디렉션이 있을 수 있으므로 실행하기 전에 전체 루프를 읽어야 합니다.)
루프를 종료한 후 Bash는 읽기 포인터를 다시 으로 이동한 다음 루프가 끝난 직후 위치에서 입력 파일 읽기를 다시 시작합니다.
스크립트에 정의된 모든 함수도 메모리에 로드되므로 스크립트의 기본 논리를 함수에 배치하고 마지막에만 호출하면 파일 수정에 대해 스크립트가 매우 안전해집니다.
#!/bin/sh
main() {
do_stuff
exit
}
main
어쨌든 스크립트를 덮어쓰면 어떤 일이 발생하는지 테스트하는 것은 그리 어렵지 않습니다.
$ cat > old.sh <<'EOF'
#!/bin/bash
for i in 1 2 3 4 ; do
# rm old.sh
cat new.sh > old.sh
sleep 1
echo $i
done
echo will this be reached?
EOF
$ cat > new.sh <<'EOF'
#!/bin/bash
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EOF
$ bash old.sh
주석 처리를 하면 rm old.sh
스크립트가 그 자리에서 변경됩니다. 주석이 없으면 새 파일이 생성됩니다. (이 예는 부분적으로 new.sh
보다 큰 것에 의존합니다 old.sh
. 마치 더 짧은 것처럼 쉘의 읽기 위치는 루프 후 새 스크립트의 끝을 지나게 됩니다.)
답변2
이전에 이 문제가 있었던 적이 있으며 이것이 문제일 수 있음을 확인할 수 있습니다. 제 경우에는 회귀 스크립트가 먼저 git pull을 실행하고 실행이 시작된 후 업데이트되어 문제가 발생할 수 있습니다.
문제는 일반적으로 쉘이 돌아가서 해석할 행이 더 있는지 확인할 때 발생합니다. 원하는 코드가 루프 내에 있는 경우에도 오류가 발생할 수 있습니다. 이를 방지하려면 다음 구조를 사용하십시오.이 게시물.
답변3
자동 실행, 자체 수정 스크립트? 그건 좋은 생각이 아니다.
더 나은 해결책은 최소한의 기능(즉, 슬레이브 스크립트의 새 버전을 설치하고 이를 주기적으로 호출하는 역할)을 갖춘 스텁 데몬을 생성하는 것입니다. 다음과 같은 것 ... (테스트되지 않음)
while true; do
# check if a patch tar file exists and if yes then do patching
if [ -f "$PATCH" ]; then
( cd /usr/local/mydaemon \
&& tar -xzf "$PATCH" \
&& rm -f "$PATCH" ) \
|| exit -1
fi
$SCRIPT
sleep 10
done