셸 구문 오류 자동 종료

7104 단어
많은 사람들 이 셸 스 크 립 트 로 간단 한 임 무 를 수행 하고 그들의 생명의 일부분 이 되 었 다.불 행 히 도 셸 스 크 립 트 가 이상 하 게 실 행 될 때 큰 영향 을 받 습 니 다.스 크 립 트 를 쓸 때 이런 문 제 를 최소 화 하 는 것 이 필요 하 다.본문 에서 나 는 bash 스 크 립 트 를 건장 하 게 만 드 는 기술 을 소개 할 것 이다.
set - u 사용
변 수 를 초기 화하 지 않 았 기 때문에 스 크 립 트 를 몇 번 이나 무 너 뜨 린 적 이 있 습 니까?나 에 게 는 여러 번.
chroot=$1
...
rm -rf $chroot/usr/share/doc

위의 코드 가 인자 에 게 실행 되 지 않 으 면 chroot 의 문서 만 삭제 하지 않 고 시스템 의 모든 문 서 를 삭제 합 니 다.뭐 공부 해요?다행히 bash 는 set - u 를 제공 합 니 다. 초기 화 되 지 않 은 변 수 를 사용 할 때 bash 를 자동 으로 종료 합 니 다.너 도 가 독성 이 좀 더 강 한 set - o nounset 을 사용 할 수 있다.
david% bash/tmp/shrink-chroot.sh
$chroot=
david% bash -u/tmp/shrink-chroot.sh
/tmp/shrink-chroot.sh: line 3: $1: unbound variable
david%
set - e 사용
당신 이 쓴 모든 스 크 립 트 의 시작 에는 set - e 가 포함 되 어야 합 니 다.이것 은 bash 에 게 알려 줍 니 다. 그러나 어떤 문구 가 비 실제 값 으로 돌아 오 면 bash 를 종료 합 니 다.사용 - e 의 장점 은 눈 덩이 를 굴 리 는 것 처럼 심각 한 오류 가 되 지 않도록 빨리 오 류 를 잡 을 수 있다 는 것 이다.더 읽 을 수 있 는 버 전: set - o errexit
사용 - e 당신 을 검사 오류 에서 해방 시 킵 니 다.만약 당신 이 검 사 를 잊 었 다 면, bash 는 당신 을 위해 이 일 을 할 것 입 니 다.하지만 당신 도 $를 사용 할 방법 이 없 나 요?명령 실행 상 태 를 가 져 옵 니 다. bash 는 0 이 아 닌 반환 값 을 얻 을 수 없 기 때 문 입 니 다.너 는 다른 구 조 를 사용 할 수 있다.
command
if [ "$?"-ne 0]; then echo "command failed"; exit 1; fi
다음으로 바 꿀 수 있 습 니 다:
command || { echo "command failed"; exit 1; }
또는 사용:
if ! command; then echo "command failed"; exit 1; fi
0 값 이 아 닌 명령 을 사용 해 야 하거나, 반환 값 에 관심 이 없다 면?command | | true 를 사용 하거나 긴 코드 를 사용 할 수 있 습 니 다. 오류 검사 기능 을 잠시 닫 을 수 있 습 니 다. 하지만 신중하게 사용 하 는 것 을 권장 합 니 다.
set +e
command1
command2
set -e
관련 문서 에 따 르 면 bash 는 파이프 의 마지막 명령 값 을 기본적으로 되 돌려 줍 니 다. 원 하지 않 는 것 일 수도 있 습 니 다.예 를 들 어 false | true 를 실행 하면 명령 이 성공 적 으로 실 행 된 것 으로 여 겨 집 니 다.이러한 명령 을 실행 실패 로 간주 하려 면 set - o pipefail 을 사용 하 십시오.
프로그램 방어 - 예상 치 못 한 일 고려
스 크 립 트 는 파일 이 없 거나 디 렉 터 리 가 만 들 어 지지 않 은 것 처럼 '의외' 계 정 에서 실 행 될 수 있 습 니 다.너 는 이런 잘못 을 예방 하 는 일 을 좀 할 수 있다.예 를 들 어 디 렉 터 리 를 만 든 후에 부모 디 렉 터 리 가 존재 하지 않 으 면 mkdir 명령 은 오 류 를 되 돌려 줍 니 다.디 렉 터 리 를 만 들 때 mkdir 명령 에 - p 옵션 을 추가 하면 필요 한 디 렉 터 리 를 만 들 기 전에 필요 한 부모 디 렉 터 리 를 만 듭 니 다.또 다른 예 는 rm 명령 이다.존재 하지 않 는 파일 을 삭제 하려 면 '토로' 하고 스 크 립 트 가 작 동 하지 않 습 니 다.(- e 옵션 을 사 용 했 기 때 문 입 니 다. 그 렇 죠?) - f 옵션 을 사용 하여 이 문 제 를 해결 할 수 있 습 니 다. 파일 이 존재 하지 않 을 때 스 크 립 트 를 계속 작업 하 게 할 수 있 습 니 다. 
파일 이름 의 빈 칸 을 처리 할 준비 가 되 어 있 습 니 다.
어떤 사람들 은 파일 이름 이나 명령 행 인자 에서 빈 칸 을 사용 합 니 다. 스 크 립 트 를 작성 할 때 항상 이 일 을 기억 해 야 합 니 다.너 는 항상 따옴표 로 변 수 를 포위 하 는 것 을 기억 해 야 한다.
if [ $filename = "foo"];
$filename 변수 가 빈 칸 을 포함 할 때 끊 습 니 다.이렇게 해결 할 수 있다.
if [ "$filename"= "foo"];
$@ 변 수 를 사용 할 때 도 인용 부 호 를 사용 해 야 합 니 다. 빈 칸 으로 구 분 된 두 개의 매개 변 수 는 두 개의 독립 된 부분 으로 해석 되 기 때 문 입 니 다.
david% foo() { for i in $@; do echo $i; done }; foo bar "baz quux"
bar
baz
quux
david% foo() { for i in "$@"; do echo $i; done }; foo bar "baz quux"
bar
baz quux
나 는 '$@' 을 사용 할 수 없 을 때 를 생각 하지 못 했 기 때문에 의문 이 있 을 때 따옴표 를 사용 하 는 것 은 잘못 이 없다.
find 와 xargs 를 동시에 사용한다 면 줄 바 꿈 대신 - print 0 을 사용 하여 파일 이름 을 분할 해 야 합 니 다.
 david% touch "foo bar"
david% find | xargs ls
ls: ./foo: No such file or directory
ls: bar: No such file or directory
david% find -print0 | xargs -0 ls
./foo bar
설 치 된 덫
작성 한 스 크 립 트 가 끊 어 지면 파일 시스템 이 알 수 없 는 상태 입 니 다.예 를 들 어 파일 잠 금 상태, 임시 파일 상태 또는 파일 을 업데이트 한 후 다음 파일 을 업데이트 하기 전에 끊 습 니 다.이 문 제 를 해결 할 수 있다 면 잠 금 파일 을 삭제 하거나 스 크 립 트 에 문제 가 생 겼 을 때 이미 알 고 있 는 상태 로 돌아 가 는 것 이 좋 습 니 다.다행히 bash 는 유 닉 스 신 호 를 받 았 을 때 명령 이나 함 수 를 실행 하 는 방법 을 제공 했다.trap 명령 을 사용 할 수 있 습 니 다.
trap command signal [signal ...]
여러 개의 신 호 를 연결 할 수 있 습 니 다 (목록 은 kill - l 로 얻 을 수 있 습 니 다). 그러나 파 국 을 정리 하기 위해 서 우 리 는 그 중의 세 가지 만 사용 합 니 다: INT, TERM, EXIT.traps 를 초기 상태 로 회복 하기 위해 서 - as 를 사용 할 수 있 습 니 다.
신호 설명
 
INT
Interrupt - 누군가가 Ctrl - C 를 사용 하여 스 크 립 트 를 종료 할 때 실 행 됩 니 다.
TERM
Terminate - 누군가가 kill 을 사용 하여 스 크 립 트 프로 세 스 를 죽 일 때 실 행 됩 니 다.
EXIT
Exit - 이것 은 가짜 신호 입 니 다. 스 크 립 트 가 정상적으로 종료 되 거나 set - e 가 잘못 되 어 종료 되 었 을 때 실 행 됩 니 다.
 
 
 
 
잠 금 파일 을 사용 할 때 이렇게 쓸 수 있 습 니 다.
if [ ! -e $lockfile ]; then
touch $lockfile
critical-section
rm $lockfile
else
echo "critical-section is already running"
fi
가장 중요 한 부분 (critical - section) 이 실행 중 일 때 스 크 립 트 프로 세 스 를 죽 이면 무슨 일이 일어 날 까요?잠 금 파일 은 거기에 버 려 지고 스 크 립 트 는 삭제 되 기 전에 다 시 는 실행 되 지 않 습 니 다.해결 방법:
if [ ! -e $lockfile ]; then
trap "rm -f $lockfile; exit"INT TERM EXIT
touch $lockfile
critical-section
rm $lockfile
trap - INT TERM EXIT
else
echo "critical-section is already running"
fi
현재 프로 세 스 를 죽 일 때 잠 금 파일 이 함께 삭 제 됩 니 다.trap 명령 에서 스 크 립 트 를 명확 하 게 종료 하지 않 으 면 스 크 립 트 는 trap 뒤의 명령 을 계속 수행 합 니 다.
최종 조건 (wikipedia)
위 에서 파일 을 잠 그 는 예 에서 하나의 최종 조건 은 지적 할 수 밖 에 없다. 이것 은 잠 금 파일 을 판단 하고 잠 금 파일 을 만 드 는 데 존재 한다.실행 가능 한 해결 방법 은 IO 리 셋 과 bash 의 noclobber (wikipedia) 모드 를 사용 하여 존재 하지 않 는 파일 로 리 셋 하 는 것 입 니 다.우 리 는 이렇게 할 수 있다.
if ( set -o noclobber; echo "$$"> "$lockfile") 2>/dev/null;
then
trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
critical-section
rm -f "$lockfile"
trap - INT TERM EXIT
else
echo "Failed to acquire lockfile: $lockfile"
echo "held by $(cat $lockfile)"
fi
더 복잡 한 문 제 는 파일 을 업데이트 하 는 것 입 니 다. 업데이트 과정 에서 문제 가 생 겼 을 때 스 크 립 트 를 좀 더 우아 하 게 걸 수 있 습 니까?당신 은 그것 이 정확하게 업데이트 되 었 는 지, 어떤 것 이 전혀 변 하지 않 았 는 지 확인 하고 싶 습 니 다.예 를 들 어 사용 자 를 추가 하 는 스 크 립 트 가 필요 합 니 다.
add_to_passwd $user
cp -a/etc/skel/home/$user
chown $user/home/$user -R
디스크 공간 이 부족 하거나 프로 세 스 가 중간 에 죽 으 면 이 스 크 립 트 에 문제 가 발생 합 니 다.이런 상황 에서 사용자 계 정 이 존재 하지 않 기 를 바 랄 수도 있 고 그의 파일 도 삭제 되 어야 할 수도 있다.
rollback() {
del_from_passwd $user
if [ -e/home/$user ]; then
rm -rf/home/$user
fi
exit
}
 
trap rollback INT TERM EXIT
add_to_passwd $user
 
cp -a/etc/skel/home/$user
chown $user/home/$user -R
trap - INT TERM EXIT
스 크 립 트 마지막 에 trap 을 사용 하여 rollback 호출 을 닫 아야 합 니 다. 그렇지 않 으 면 스 크 립 트 가 정상적으로 종 료 될 때 rollback 이 호출 됩 니 다. 그러면 스 크 립 트 는 아무것도 하지 않 은 것 과 같 습 니 다.
원자 화 를 유지 하 다
또한 디 렉 터 리 에 있 는 많은 파일 을 업데이트 해 야 합 니 다. 예 를 들 어 URL 을 다른 사이트 의 도 메 인 이름 으로 다시 써 야 합 니 다.그렇게 지도 모른다, 아마, 아마...
for file in $(find/var/www -type f -name "*.html"); do
perl -pi -e 's/www.example.net/www.example.com/' $file
done
절반 으로 수정 하면 스 크 립 트 에 문제 가 생기 면 일 부 는 www. example. com 을 사용 하고, 다른 일 부 는 www. example. net 을 사용한다.백업 과 trap 으로 해결 할 수 있 지만 업그레이드 과정 에서 사이트 URL 은 일치 하지 않 습 니 다.
해결 방법 은 이 변 화 를 원자 조작 으로 만 드 는 것 이다.먼저 데 이 터 를 복사 본 으로 만 들 고 복사 본 에서 URL 을 업데이트 한 다음 에 복사 본 으로 현재 작업 중인 버 전 을 교체 합 니 다.복사 본과 작업 버 전 디 렉 터 리 가 같은 디스크 파 티 션 에 있 는 지 확인 해 야 합 니 다. 그러면 리 눅 스 시스템 의 장점 을 이용 할 수 있 습 니 다. 디 렉 터 리 를 이동 하 는 것 은 디 렉 터 리 가 가리 키 는 inode 노드 만 업데이트 할 수 있 습 니 다.
cp -a/var/www/var/www-tmp
for file in $(find/var/www-tmp -type -f -name "*.html"); do
perl -pi -e 's/www.example.net/www.example.com/' $file
done
mv/var/www/var/www-old
mv/var/www-tmp/var/www

좋은 웹페이지 즐겨찾기