저는 Bash 4.3.48(1)을 사용하고 있으며 테스트 VPS 시스템에서 다음 명령 패턴을 실행하고 있습니다.
rm -rf ${drt}/${pma}*
이 명령은 전체 운영 체제(Ubuntu)를 제거합니다. 이는 cd /
다음 오류만 반환하는 실행을 통해 분명해집니다 .
bash: cd /: 해당 파일이나 디렉터리가 없습니다.
더 자세히 살펴보면 위의 원래 명령에서 변수가 선언되지 않았기 때문에 이런 일이 발생합니다.
~/repoName/assignments_variables.sh
내 보낸 변수 목록(drt
및 포함)이 포함 된 파일을 만들었습니다pma
.대신에
source /etc/bash.bashrc
running 을 실행했는데/etc/bash.bashrc
, 이는 파일이 현재 세션에만 관련되어 있기 때문에 잘못된 것입니다(나중에 하위 세션에서 데이터를 실행하는 파일 자체를 실행하는 데 사용될 수는 있지만).
이제 잘못된 경로가 문제의 일반적인 원인이라는 것이 분명해졌습니다. 좀 더 깊이 파고들어 다음 질문을 드리고 싶습니다.
rm -rf
잘못된 변수 확장이 무시되고 계속 진행되는 이유는 무엇입니까 /*
? rm -rf
디렉토리를 삭제해야 한다는 것을 알고 있습니다.오직발견된 경우 존재하지 않는 디렉터리에 대한 부분 경로에 의존해서는 안 됩니다( /*
운영 체제가 제거될 수 있음). rm -rf
앞으로 발생할 수 있는 유사한 잘못된 경로 상황(잘못된 변수 확장으로 인해)을 처리하기 위해 이 명령을 어떻게 개선할 수 있습니까 ?
Bash가 (지시문을 실행 취소할 때까지) 빈 변수를 확장하지 않도록 하는 Bash 지시문이 있습니까?
이것을 강조하고 싶습니다. 저는 보통 백업만 하는 것이 아니라 이중 백업을 합니다. 이것은 실제로 백그라운드에서 x3 백업을 사용하는 테스트 환경입니다.
답변1
변수가 존재하지 않으면 무엇이 확장되나요?
시도해 봅시다(에코에 주목하세요):
$ unset drt pma
$ echo rm -rf ${drt}/${pma}*
rm -rf /bin /boot /dev /etc /hello /home /initrd.img /lib /lib32 /lib64 /libx32 /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old
이것이 전체 시스템입니다(이미 확인했듯이).
문제는 쉘에 의한 확장입니다.
방지
이런 일이 발생하지 않도록 하려면 어떻게 해야 합니까?
쉘이 빈 변수를 확장하는 것을 방지합니다. var가 비어 있거나 설정되지 않은 경우
확장이 발생합니다.${var:?message}
message
그리고실행이 중지되었습니다(치명적인 오류). 테스트를 받아보세요:
$ echo rm -rf "${drt:?Missing variable drt}"/"${pma:?Missing variable pma}"*
그리고 이 솔루션은 POSIX와 호환됩니다.
답변2
이미 지적한 대로 이 두 변수 drt
는 pma
스크립트 환경에 존재하지 않습니다. 즉, 셸은 이를 빈 문자열로 확장합니다. 생성된 명령은 다음과 같습니다 rm -rf /*
( *
루트 디렉터리에서 사용 가능한 모든 명령으로 더 확장됨).
루트로 실행해도 루트 디렉터리 자체는 삭제되지 않습니다 rm -rf /*
. 또한 나는 (그리고Jesse_b누가 지적해줬나요) 오류 메시지가 좀 헷갈리네요
bash: cd /: No such file or directory
내가 직접 이 오류 메시지를 발생시킬 수 있었던 유일한 방법은 명령을 실행하는 것입니다 "cd /"
(따옴표 참고). 실제로 삭제된 경우 /
오류 메시지는 다음과 같습니다.
bash: cd: /: No such file or directory
이삭이 몇 가지 팁을 제공했습니다.앞으로 이런 일을 방지하는 방법은 있지만 제안 사항만 말씀드리겠습니다.
- 루트 쉘 프롬프트에서 작업하지 마십시오.
- 루트 권한이 필요한 스크립트를 작성하는 경우
sudo
다음을 사용하십시오 .특정한이러한 상승된 명령은 실제로 필요합니다. 전체 스크립트를 루트로 실행하지 마십시오.특히개발 과정에는 없습니다. /etc/bash.bashrc
일반적으로 어떤 이유로든 연락할 필요가 없으며 명시적으로 제공할 필요도 없습니다. 환경 변수는 다음에서 생성할 수 있습니다.- 사용자 자신의 것
$HOME/.bashrc
(대화형 세션에서 스크립트를 실행하려는 경우) 또는 - 스크립트 자체에서 또는
- 별도의 파일에(다음과 같이 작성됨)~을 위한script)는 스크립트에서 명시적으로 소스를 얻습니다.
- 환경 변수가 가리키는 파일에서
BASH_ENV
.
- 사용자 자신의 것
이 경우에 당신을 구할 수 있는 또 다른 보안 조치는 set -u
( nounset
) 아래에서 스크립트를 실행하고 set -e
( ) errexit
아래에서 스크립트를 실행하는 것입니다. 쉘 nounset
옵션은 설정되지 않은 변수의 확장을 오류로 처리하고 errexit
명령이 0이 아닌 종료 상태로 종료되면 즉시 쉘 세션을 종료합니다.
스크립트
#!/bin/bash -ue
echo "$hello"
echo "world"
출력되지 않고 world
대신 메시지가 발생합니다.
script.sh: line 3: hello: unbound variable
종료 전. ( -u
종료 작업을 수행 -e
하지만 아무 작업도 수행하지 않습니다.이 간단한 예에서는).
답변3
변수에는 보간 세트가 없으므로 glob이 실행되고 rm -rf /*
그 아래의 모든 항목이 /
깨집니다.
오류 검사를 추가합니다. 또한 인용하십시오. "$variables"
그렇지 않으면 쉘이 이를 분할하여 $variables
공백이나 기타 문제가 있는 문자가 포함되면 어리석은 일이 발생할 수 있기 때문입니다. 특히 a의 rm -rf
경우.
if [[ -z "$drt" ]]; then
echo >&2 "drt variable not set ??"
exit 1
fi
if [[ -z "$pma" ]]; then
echo >&2 "pma variable not set ??"
exit 1
fi
rm -rf "${drt}"/"${pma}"*
(또는 문제가 덜한 언어로 프로그램...)