정보
-
업무명 : 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (2)
-
작성자 : 박진만
-
작성일 : 2020-03-22
-
설 명 :
-
수정이력 :
요약
[특징]
-
쉘 스크립트 개발자가 빠지기 쉬운 함정을 소개.
[활용 자료]
-
없음
[자료 처리 방안 및 활용 분석 기법]
-
없음
[사용 언어]
-
Bash Script
내용
-
이 페이지는 Bash 프로그래머가 흔히 발생하는 오류에 대해 요약하였습니다.
-
아래의 모든 코드의 예시는 어떤 결함을 가지고 있습니다.
-
들어가기에 앞서 기본적으로 따옴표 (“)를 항상 사용하고, 또 절대로 단어 분할을 사용하지 않는다면 대부분의 함정에 빠지지 않을 수 있습니다.
-
따옴표를 사용하지 않았을 때의 단어 분할이 되는 이유는 기본적으로 켜져 있는, Bourne쉘에서 상속받은 레거시 코드의 설계 오류 때문입니다.
-
따라서 하단에서 소개할 결함의 대부분은 이 따옴표와 단어 분할에 관련된 문제가 대부분입니다.
21. for i in {1..10}; do ./something &; done
-
;을 & 뒤에 사용할 수 없습니다. 아래의 코드처럼 ;을 제거하십시오.
for i in {1..10}; do ./something & done
-
또는 다음과 같이 합니다.
for i in {1..10}; do
./something &
done
-
이는 &이 ;같은 명령을 종료시키는 역할로 기능하고 있기 때문입니다. 따라서 이 두 가지를 혼합 할 수 없습니다.
22. cmd1 && cmd2 || cmd3
-
&&및 ||을 if ... then ... else ... fi바로 가기 구문으로 사용하는 사람이 있습니다. 일반적으로 많은 경우에 이것은 완벽하게 안전합니다.
[[ -s $errorlog ]] && echo "Uh oh, there were some errors." || echo "Successful."
-
그러나 일반적으로 이 구성은 if ... fi와 다른 방식이라 할 수 있습니다.
-
&& 또한 종료 상태를 생성 한 후 명령이 전달됩니다.
-
즉 만약 종료 상태 "true"(0)이 아닌 경우에는 || 다음의 명령도 작동하지 않게 됩니다. 예를 들면 다음과 같습니다.
i=0
true && ((i++)) || ((i--))
echo $i # Prints 0
-
여기에 무슨 일이 있을까요?
-
i가 1이 되어야 할 것처럼 보이지만 실제로는 0이 출력됩니다.
-
왜냐하면 그것은 i++그리고 i--이 모두 실행 되었기 때문입니다.
-
((i++))명령은 종료 상태를 가지고 있고 그 종료 상태는 괄호 안의 C 언어 같은 평가의 표현에서 제공됩니다.
-
이 표현의 값은 0 ( i초기 값)에서 C에서 int 형의 0는 false 받아 들여지고 있습니다.
-
즉, ((i++))( i가 0 일 때) 종료 상태는 1이고 (즉 false) 그래서 ((i--))명령도 실행되는 것입니다.
-
이것은 전치 증가 연산자를 이용한 경우 ++i의 종료 상태가 true가 아니기 때문에 발생하지 않습니다
i=0
true && (( ++i )) || (( --i ))
echo $i # Prints 1
-
그러나 이것은 예의 포인트를 잃고 있습니다.
-
이것은 살얼음판 위에서 동작하고 있으며, 만약 실패할 가능성이 있다면, x && y || z if y에 의지 할 수 없습니다! (이 예에서는 만약 i을 0으로 바꾸어 -1에서 초기화했다면 실패하게 됩니다.)
-
만약 안전성이 필요한 경우 또는 이것이 어떻게 움직이는 지 확실하지 않는다면, 혹은 어딘가 절 무언가가 명확하지 않은 경우, 단순 if ... fi구문을 프로그램 내부에서 사용하십시오.
i=0
if true; then
((i++))
else
((i--))
fi
echo $i # Prints 1
-
이 절은 Bourne Shell 마찬가지 그것을 나타내는 코드가 여기에 있습니다.
true && { echo true; false; } || { echo false; true; }
23. echo "Hello World!"
-
여기에서 문제는 대화 형 Bash 쉘에서 다음과 같은 오류가 발생하는 것입니다.
-
이것은 대화 형 쉘의 기본 설정에서는 Bash는 csh-style 명령 히스토리 확장을 !을 이용하여 사용하기 때문에 발생합니다. 이것은 쉘 스크립트의 문제가 아니라 대화 형 쉘 만에서 발생하는 문제입니다.
-
이 현상에 대한 해결법은 아래와 같습니다.
$ echo "hi\!"
hi\!
-
가장 간단한 해결 방법은 histexpand옵션을 설정하지 않는 것입니다. 이것은 set +H나 set +o histexpand에서 실시 할 수 있습니다.
-
그런데, 여기서 문제입니다. 왜 histexpand는 작은 따옴표보다 우선되는 것일까요? 나는 mp3 파일을 조작 할 때 개인적으로이 문제가 발생했습니다.
mp3info -t "Don't Let It Show" ...
mp3info -t "Ah! Leah!" ...
-
작은 따옴표를 사용하는 것은 매우 불편합니다.
-
그러나 큰 따옴표를 사용하면 명령 히스토리 확장의 문제에 해당됩니다. (그리고 파일이 아포스트로피 및 !모두를 포함하는 것을 상상해보십시오. 인용에 의한 구분은 심한 것입니다.)
-
정말 명령 히스토리 확장을 사용한 적이 없기 때문에 개인적인 취향 치아 ~/.bashrc에서 명령 히스토리 확장을 비활성화하는 것입니다.
-
이 경우 다음의 명령에 유효합니다 :
echo 'Hello World!'
-
또는
set +H
echo "Hello World!"set +Hecho "Hello World!"
-
또는
histchars=
-
많은 사람들은 간단하게 ~/.bashrc안쪽에 set +H또는 set +o histexpand설정 명령 히스토리 확장을 해제하는 것을 선택합니다.
-
이것은 개인적인 취향이므로 어떤 방법이 여러분에게 맞는 있는지 선택하면 됩니다.
-
다른 해결 방법으로는 다음이 있습니다 :
exmark='!'
echo "Hello, world$exmark"
24. for arg in $ *
-
Bash (모든 Bourne 쉘)은 위치 매개 변수의 목록을 하나씩 볼 수있는 특별한 구문을 가지고 있습니다.
-
그리고 $* 같은 경우 위와 같은 일을 수행하지 않습니다. 또한 $@도 그렇지는 않습니다. 모두 스크립트 만약 파라미터에서 단어 목록에 확장하고 다른 단어로 각각의 매개 변수에 배포하지 않습니다.
-
올바른 구문은 다음과 같습니다.
for arg in "$@"
# Or simply:
for argfor arg in "$@"# Or simply:for arg
-
위치 매개 변수에서 루프 할 스크립트에서 다음과 같게하는 것이 일반적이며, for arg기본적으로 for arg in "$@"같은 느낌입니다.
-
여기서 큰 따옴표 된 "$@"각각의 변수를 하나의 단어로 취급할 수 있습니다.
-
여기에 예제가 있습니다.
# 올바르지 않은 방법
for x in $*; do
echo "parameter: '$x'"
done# Incorrect version for x in $*; do echo "parameter: '$x'" done
$ ./myscript 'arg 1' arg2 arg3
parameter: 'arg'
parameter: '1'
parameter: 'arg2'
parameter: 'arg3'
-
이것은 다음과 같이 작성해야합니다.
#올바른 방법
for x in "$@"; do
echo "parameter: '$x'"
done
$ ./myscript 'arg 1' arg2 arg3
parameter: 'arg 1'
parameter: 'arg2'
parameter: 'arg3'
25. function foo ()
-
이것은 일부 Shell에서 작동합니다만, 작동하지 않을수도 있습니다.
-
왜냐하면 함수를 정의 할 때 키워드 기능을 () 결합하는 것이 아니기 때문입니다.
-
Bash (적어도 일부 버전에서) 두 가지를 혼합하는 것을 허용하고 있습니다.
-
그러나 대부분의 Shell은 이를 허용하지 않습니다.
-
일부 Shell은 위의 foo함수를 허용하지만, 이동성 극대화를 위해 다음과 같은 것을 권장합니다.
foo() {
...
}
26. echo "~"
-
물결표 확장은 ~이 인용되지 않은 경우에만 유효합니다. 이 예제에서는 echo는 ~을 사용자의 홈 디렉토리 경로가 아니라 ~으로 표준 출력에 기록합니다
-
인용 된 경로의 매개 변수로 사용자 홈 디렉토리의 상대 경로를 표현하려면 ~대신 $HOME을 사용해야합니다. 예를 들어 $HOME이 /home/my photos경우를 예로 들어 봅시다.
"~/dir with spaces" # "~/dir with spaces"
~"/dir with spaces" # "~/dir with spaces"
~/"dir with spaces" # "/home/my photos/dir with spaces"
"$HOME/dir with spaces" # "/home/my photos/dir with spaces"
27. local varname = $ (command)
-
로컬 변수를 함수 안에서 선언 할 때 local명령으로 자신의 권한에 따라 행동합니다.
-
이것은 이상하게도 다른 행에 영향을 줄 수 있습니다.
-
예를 들어, 명령 치환의 종료 상태 ( $?)을 취득하려고해도 할 수 없습니다. local종료 상태가 그것을 대체하게됩니다.
-
다음과 같이 다른 명령을 사용하는 것이 좋습니다.
local varname
varname=$(command)
rc=$?local varname varname=$(command) rc=$?
-
다음 함정에서 구문에 관한 다른 문제를 설명합니다.
28. export foo = ~ / bar
-
물결표 열기 (사용자 이름과 함께 또는 사용자 이름없이) 물결표가 문자열의 처음 또는 슬래시 후, 또는 물결표 만 때만 발생하는 것이 보증되고 있습니다.
-
또한 =의한 변수에 대입 직후 물결이 나타나는 경우도 보증되고 있습니다.
-
그러나 export나 local명령은 이러한 대입을 구성하지 않습니다.
-
즉, 어떤 쉘 (Bash 같은)은 export foo=~/bar물결표의 전개가 이루어집니다.
foo=~/bar; export foo # Right!
export foo="$HOME/bar" # Right!
29. sed 's / $ foo / good bye /'
-
작은 따옴표 안에, $foo같은 bash 매개변수가 할당되지 않습니다.
-
왜냐하면 작은 따옴표 목적으로 $ 같은 문자를 쉘로부터 보호하기 위해 내장된 기능 때문입니다.
-
이 경우 작은 따옴표를 큰 따옴표로 변경하면 해결 됩니다.
foo="hello"; sed "s/$foo/good bye/"
-
그러나 큰 따옴표를 사용하는 경우 더 이스케이프해야 할 수도 있음을 기억하십시오
30. tr [AZ] [az]
-
여기에는 (적어도) 3 가지 잘못된 점이 있습니다. 첫 번째 문제는 [A-Z]그리고 [a-z]쉘은 glob 처럼 보이는 것입니다.
-
만약 현재 디렉토리에 글자의 파일 이름의 파일이 없으면 명령이 작동되는 것처럼 보이는 것입니다. 하지만 해 보면 실행 결과는 실패합니다.
-
두 번째 문제는 tr의 반드시 올바른 작성은 없다는 것입니다.
-
세 번째 문제는 로케일 의존에서 A-Z또는 a-z기대 한대로 26 문자 ASCII 문자열을 미치지 않을 수 있다는 것입니다. 실제로 일부 로케일에서는 z알파벳의 중간 문자입니다! 이 해결책은 무엇을하고 싶은가에 따라 달라집니다.
# 26 문자 라틴 배열을 변경하고자하는 경우에는 이것을 사용해주세요
LC_COLLATE=C tr A-Z a-z
# 사용자가 기대하는 것에 가까운 로캘 종속 변환을 원하는 경우에는 이것을 사용해주세요
tr '[:upper:]' '[:lower:]'
[다른편 링크]
-
1편
-
2편
참고 문헌
[논문]
- 없음
[보고서]
- 없음
[URL]
- 없음
문의사항
[기상학/프로그래밍 언어]
- sangho.lee.1990@gmail.com
[해양학/천문학/빅데이터]
- saimang0804@gmail.com
본 블로그는 파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음
'프로그래밍 언어 > Shell Script' 카테고리의 다른 글
[ShellScript] 쉘 스크립트 사용법 (변수를 사용하는 법) (2) | 2020.07.17 |
---|---|
[ShellScript] 쉘 스크립트를 사용할 때 코드 작성 팁 (2) | 2020.07.17 |
[Shell Script] 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (2) (0) | 2020.03.22 |
[Shell Script] 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (1) (2) | 2020.03.21 |
[Shell Script] 쉘 스크립트에서 문자열 길이를 계산하는 4가지 방법 (0) | 2020.03.13 |
최근댓글