정보
-
업무명 : 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (2)
-
작성자 : 박진만
-
작성일 : 2020-03-22
-
설 명 :
-
수정이력 :
요약
[특징]
-
쉘 스크립트 개발자가 빠지기 쉬운 함정을 소개.
[활용 자료]
-
없음
[자료 처리 방안 및 활용 분석 기법]
-
없음
[사용 언어]
-
Bash Script
내용
-
이 페이지는 Bash 프로그래머가 흔히 발생하는 오류에 대해 요약하였습니다.
-
아래의 모든 코드의 예시는 어떤 결함을 가지고 있습니다.
-
들어가기에 앞서 기본적으로 따옴표 (“)를 항상 사용하고, 또 절대로 단어 분할을 사용하지 않는다면 대부분의 함정에 빠지지 않을 수 있습니다.
-
따옴표를 사용하지 않았을 때의 단어 분할이 되는 이유는 기본적으로 켜져 있는, Bourne쉘에서 상속받은 레거시 코드의 설계 오류 때문입니다.
-
따라서 하단에서 소개할 결함의 대부분은 이 따옴표와 단어 분할에 관련된 문제가 대부분입니다.
11. if [ [ a = b ] && [ c = d ] ]; then ...
-
위 예제의 경우 if와 C 언어 같은 조건절의 종류의 사이에 위치하는 구문 마커가 없습니다.
-
그리고 그룹화도 없습니다. C 언어 같은 if명령을 사용하는 것은 불가능 하고 대신 괄호 []이지 대체하는 것으로는 Bash 명령을 수행 할 수 없습니다!
-
만약 합성 조건식을 표현하고자 한다면 다음과 같이 하시면 됩니다.
if [ a = b ] && [ c = d ]; then ...
-
if다음에 &&(논리적 AND 단축 된 평가 식) 연산자로 연결 한 2 개의 명령이 있다는 것을 기억하십시오. 이것은 아래의 것과 완전히 동일합니다.
if test a = b && test c = d; then ...
-
만약 첫 번째 test명령이 false로 응답해도 if문 본문에는 아무것도 입력되지 않습니다.
-
즉 true를 반환할 때 두 번째 test명령이 실행됩니다. 그리고 true를 돌려주는 경우에는 if문 본문은 입력됩니다. (C 프로그래머는 &&익숙한 것입니다 .Bash 같은 단축 된 평가 식을 사용합니다. 마찬가지로 || 도 OR연산이 단축 된 평가 식입니다.)
-
[[ 키워드 &&의 사용을 허용하기 때문에 다음과 같이 쓸 수 있습니다 :
if [[ a = b && c = d ]]; then ...
-
평가 연산자와 함께 사용되는 test에 대해서는 함정 # 6을 참조하십시오.
12. read $foo
-
read명령은 변수 이름 앞에 $를 사용할 수 없습니다. 만약 foo라는 변수에 데이터를 넣고 싶다면 다음과 같이해야합니다
read foo
-
혹은 더 안전하게 다음과 같이합니다.
IFS= read -r foo
-
read $foo입력 행을 로드하려고 하고 그것을 $foo 안에 있는 이름에 대입하려고 하는 작업을 수행하고자 할 때, 즉 당신이 foo다른 변수에 참조되도록 의도하는 경우에 유용합니다. 그러나 대부분의 경우 이것은 단순히 버그입니다.
13. cat file | sed s/foo/bar/ > file
-
파일에서 읽기, 쓰기를 같은 파이프 라인에서 수행 할 수 없습니다.
-
경우에 따라서 그 파이프 라인이 무엇을 할 것인가에 따라, 파일을 덮어버리게 될 수 있습니다.
-
또는 그것이 가능 디스크 공간을 가득 찰 때까지 크게 될 수 있으며, OS의 파일 크기 제한에 도달하거나 할당량 제한에 도달 등하는 것입니다.
-
만약 안전하게 파일에 추가하고 싶다면, 파일의 마지막에 추가하는 것 외에, 텍스트 편집기를 사용하십시오.
printf %s\\n ',s/foo/bar/g' w q | ed -s file
-
만약 텍스트 편집기에서 무언가를 할 경우 어느 시점에서 만들어진 임시 파일이 필요합니다. (*) 예를 들어 다음 예제는 모범 사례라 할 수 있습니다.]
sed 's/foo/bar/g' file > tmpfile && mv tmpfile file
-
아래의 예는 GNU sed 4.X 계에서만 작동합니다 :
sed -i 's/foo/bar/g' file(s)
-
다음 대체 명령은 perl 5.X 시스템을 필요로합니다 (즉 GNU sed 4.X 계에서는 사용할 수 없습니다.)
perl -pi -e 's/foo/bar/g' file(s)
-
(*) : moreutils 의 sponge 명령은 그 설명서에서 다음 예를 사용하고 있습니다 :
sed '...' file | grep '...' | sponge file
-
mv명령의 불가분성 이외에 임시 파일을 이용하는 것보다 이 버전은 모든 데이터를 파일을 열고 쓰기 전에 "담가"입니다 (실제 문서의 표현입니다!).
-
처리 시점에서 원본 파일의 복사본이 디스크에 없기 때문에 이 버전은 프로그램이 쓰기 연산에서 충돌 한 경우에는 데이터 손실을 일으 킵니다.
-
임시 파일과 mv를 사용하는 것은 전원 차단이나 시스템 충돌시에도 극히 적은 위험 만 있습니다. 전원 차단시에도 새 파일이나 기존 파일 중 하나가 남도록 100 % 보장하려면 mv전에 sync해야합니다.
14. echo $foo
-
위의 문제가 없어 보이는 이 명령은 엄청난 혼란을 일으킵니다. 왜냐하면 $foo이 인용 되지 않고, 단어 분리를 일으킬 뿐 아니라 파일의 glob 도 생기기 때문입니다.
-
이것은 Bash 프로그래머들에게 하여금 그들의 변수가 잘못된 값을 포함시키는 것처럼 착각시킵니다.
-
비록 실제로는 값에 문제가 없었다고 해도. 단어 분할 또는 파일 이름 전개는 무슨 일이 일어나고 있는지 혼란될 수 있습니다.
msg="Please enter a file name of the form *.zip"
echo $msg
-
이 메시지는 두 단어로 분할되고, glob 역시 전개됩니다, 예를 들어 *.zip같은 것도. 당신의 사용자가 이 메시지를 봤을 때 어떻게 생각할까요?
Please enter a file name of the form freenfss.zip lw35nfss.zip
-
예시를 보여드리겠습니다.
var="*.zip" # var contains an asterisk, a period, and the word "zip"
echo "$var" # writes *.zip
echo $var # writes the list of files which end with .zipvar="*.zip" # var contains an asterisk, a period, and the word "zip"echo "$var" # writes *.zipecho $var # writes the list of files which end with .zip
-
사실 여기에서 echo명령은 절대적인 안전성이있는 상태에서 사용될 수 없습니다. 예를 들어 만약 변수가 -n를 포함하는 경우 echo는 그것을 표시되는 데이터가 아니라 옵션으로 해석
-
합니다. 변수의 값을 표시하는 가장 절대적으로 확실한 방법은 printf을 사용하는 것입니다.
printf "%s\n" "$foo"
15. $foo=bar
-
$변수 이름 앞에 붙여 변수를 할당 할 수 없습니다.
16. foo = bar
-
변수에 값을 할당 할 때 공백 주위에 =을 쓸 수 없습니다. C는 가능하지만 당신이 foo = bar쓴 때, shell은 그들을 3 개의 단어로 나눕니다.
-
마찬가지로 다음 예제도 잘못된 것입니다 :
foo= bar # 잘못된 코드
foo =bar # 잘못된 코드
$foo = bar; # 완전히 잘못된 코드
foo=bar # 옳은코드
foo="bar" # 매우 좋은 코드foo= bar # 잘못된 코드foo =bar # 잘못된 코드$foo = bar; # 완전히 잘못된 코드foo=bar # 옳은코드foo="bar" # 매우 좋은 코드
17. echo <<EOF
-
여기에서 문서 스크립트에서 텍스트 데이터의 큰 블록을 포함하는 유용한 도구입니다. 이것은 스크립트의 텍스트 행을 명령의 표준 출력으로 리디렉션 합니다.
-
불행히도 이것은 echo 표준 입력에서 읽을 수 없습니다.
# 잘못된 예시
echo <<EOF
Hello world
How's it going?
EOF
# 좋은 예시
cat <<EOF
Hello world
How's it going?
EOF
# Or, use quotes which can span multiple lines (efficient, echo is built-in):
echo "Hello world
How's it going?"# 잘못된 예시echo <<EOFHello worldHow's it going?EOF# 좋은 예시cat <<EOFHello worldHow's it going?EOF# Or, use quotes which can span multiple lines (efficient, echo is built-in):echo "Hello worldHow's it going?"
-
따옴표를 이렇게 사용하는 것은 문제가되지 않습니다, 모든 쉘에서 제대로 동작합니다.
-
그러나 행의 나눔을 스크립트에 떨어뜨리는 것은 불가능합니다.
-
처음이자 마지막 행에 구문 태그가 있습니다. 만약 쉘 구문에서 만지지 행이있는 경우에는 또는 cat명령을 spawn하고 싶지 않다면 여기에 대안이 있습니다
# 대안으로 printf 를 사용할 수도 있음
printf %s "\
Hello world
How's it going?# 대안으로 printf 를 사용할 수도 있음printf %s "\Hello worldHow's it going?
-
위의 예시에서 첫 번째 줄의 \텍스트 블록의 시작 부분에 불필요한 줄 바꿈을 추가하는 것을 방지합니다.
-
마지막 줄에 줄 바꿈이 포함됩니다 (마지막 쿼터는 줄 바꿈과 함께 있기 때문입니다.).
-
printf형식 인수의 \n를 사용하지 않는 것은 printf이 마지막으로 불필요한 행을 추가하는 것을 방지합니다.
-
\트릭은 작은 따옴표 내에서 작동하지 않습니다. 만약 텍스트 덩어리 주위에 작은 따옴표를 사용하고 / 릏 사용할 필요가있는 경우에는 2 개의 옵션이 있습니다
printf %s \
'Hello world'
printf %s 'Hello world
18. su -c 'some command'
-
이 구문은 거의 맞습니다. 그러나 문제는 많은 플랫폼에서 su는 -c 인수를 취하 만, 이 경우 쉘에서 결과가 나도지 않습니다.
$ su -c 'echo hello'
-
-c 'some command'쉘에 전달하려면 사용자 이름을 -c앞에 붙여야합니다.
su root -c 'some command' # Now it's right.
-
이 명령을 사용한 경우에는 su이 root라는 사용자 이름이 있다고 간주합니다 그러나 Shell 명령을 나중에 전달하려는 경우 반드시 사용자 이름을 제공해야합니다.
19. cd /foo; bar
-
만약 cd명령에서 오류를 확인하지 않는 경우, bar잘못된 장소에서 실행되고 종료 될 것입니다. 예를 들어 만약 bar이 rm -f *있다면, 이것은 대단한 재해를 야기 할 수 없습니다.
-
cd명령에서 오류가 있는지 항상 확인하는 것이 필요합니다. 가장 간단한 작업을 수행하는 방법은 다음과 같습니다.
cd /foo && bar
-
만약 cd후에 하나 이상의 명령이 있는 경우에는 아래의 예시가 바람직합니다.
cd /foo || exit 1
bar
baz
bat ... # Lots of commands.cd /foo || exit 1barbazbat ... # Lots of commands.
-
cd 디렉토리의 변경의 실패를 "bash : cd : / foo : No such file or directory"와 같은 표준 오류 출력 메시지와 함께 알려줍니다. 하지만 표준 출력에 어떤 메시지를 추가하고 싶다면 다음과 같이 명령 그룹화 사용할 수 있습니다.
cd /net || { echo "Can't read /net. Make sure you've logged in to the Samba network, and try again."; exit 1; }
do_stuff
more_stuffcd /net || { echo "Can't read /net. Make sure you've logged in to the Samba network, and try again."; exit 1; }do_stuffmore_stuff
-
{과 echo의 사이에 공간이 필요한 것, 그리고 }에 닫기 전에 ;이 필요한 것을 기억하십시오
-
0이 아닌 코드를 반환 명령 중 하나에 스크립트를 도중에 중단하기 위해 "set -e ' 를 사용하는 것을 선호하는 사람들도 있지만, 이를 제대로 사용하는 것은 조금 까다롭습니다.
-
그런데 많은 Bash 스크립트 내부에서 디렉토리를 변경하고 있다면, Bash는 pushd, popd, dirs에 대한 도움말을 읽는 것이 좋습니다. 아마 당신이 쓴 cd및 pwd관리하는 모든 코드는 완전히 필요없는 것들 일 것이다.
-
이것을 말하기는 아래의 코드를 보고 비교해봅시다.
find ... -type d -print0 | while IFS= read -r -d '' subdir; do
here=$PWD
cd "$subdir" && whatever && ...
cd "$here"
done
비교 대상입니다 :
find ... -type d -print0 | while IFS= read -r -d '' subdir; do
(cd "$subdir" || exit; whatever; ...)
donefind ... -type d -print0 | while IFS= read -r -d '' subdir; do here=$PWD cd "$subdir" && whatever && ... cd "$here"done비교 대상입니다 :find ... -type d -print0 | while IFS= read -r -d '' subdir; do (cd "$subdir" || exit; whatever; ...)done
-
루프의 다음 반복은 cd성공 실패에 관계없이 정상 위치로 돌아갑니다.
-
수동으로 다시 조작할 필요는없고, 또한 끝나지 않는 다른 조건식의 사용을 방지 &&로직의 반복으로 채우기도 없습니다. Subshell 버전은 간단하고 깨끗합니다
20. [ bar == "$foo" ]
-
==연산자는 [명령에 유효하지 않습니다. 대신, =또는 [[를 사용하십시오.
[ bar = "$foo" ] && echo yes
[[ bar == $foo ]] && echo yes
참고 문헌
[논문]
- 없음
[보고서]
- 없음
[URL]
- 없음
문의사항
[기상학/프로그래밍 언어]
- sangho.lee.1990@gmail.com
[해양학/천문학/빅데이터]
- saimang0804@gmail.com
본 블로그는 파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음
'프로그래밍 언어 > Shell Script' 카테고리의 다른 글
[ShellScript] 쉘 스크립트를 사용할 때 코드 작성 팁 (2) | 2020.07.17 |
---|---|
[Shell Script] 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (3) (0) | 2020.03.23 |
[Shell Script] 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (1) (2) | 2020.03.21 |
[Shell Script] 쉘 스크립트에서 문자열 길이를 계산하는 4가지 방법 (0) | 2020.03.13 |
[Shell Script] 쉘 스크립트에서 수치 계산 소개 (0) | 2020.03.11 |
최근댓글