런타임 대체 쉘 스크립트

런타임 대체 쉘 스크립트

"패치" 스크립트를 실행하는 보드가 있습니다. 패치 스크립트는 항상 백그라운드에서 실행되며 다음 의사 코드를 실행하는 셸 스크립트입니다.

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 관련 콘텐츠입니다.

스크립트가 들어 있을 때/opt/patch.shtar를 찾으려면 다음을 수행합니다.

tar -xf /opt/update.tar -C /mnt/update
mv /mnt/update/patch.sh /opt/patch.sh
exec /opt/patch.sh

자신을 다른 스크립트로 대체하고 동일한 위치에서 실행합니다. 이 작업을 수행하는 데 문제가 있나요?

답변1

파일이 내부 쓰기로 대체되면(inode는 변경되지 않음) 파일을 연 프로세스는 파일을 읽을 때 새 데이터를 보게 됩니다. 이전 파일의 링크를 해제하고 동일한 이름의 새 파일을 생성하여 이전 파일을 교체하면 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

관련 정보