[Shell Script] 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (3)

정보

  • 업무명     : 쉘 스크립트 (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

 

  • 여기에 무슨 일이 있을까요?

  • i1이 되어야 할 것처럼 보이지만 실제로는 0이 출력됩니다.

  • 왜냐하면 그것은 i++그리고 i--이 모두 실행 되었기 때문입니다.

  • ((i++))명령은 종료 상태를 가지고 있고 그 종료 상태는 괄호 안의 C 언어 같은 평가의 표현에서 제공됩니다.

  • 이 표현의 값은 0 ( i초기 값)에서 C에서 int 형의 0false 받아 들여지고 있습니다.

  • , ((i++))( i0 일 때) 종료 상태는 1이고 (false) 그래서 ((i--))명령도 실행되는 것입니다.

  • 이것은 전치 증가 연산자를 이용한 경우 ++i의 종료 상태가 true가 아니기 때문에 발생하지 않습니다

i=0

true && (( ++i )) || (( --i ))

echo $i # Prints 1

 

  • 그러나 이것은 예의 포인트를 잃고 있습니다.

  • 이것은 살얼음판 위에서 동작하고 있으며, 만약 실패할 가능성이 있다면, x && y || z if y에 의지 할 수 없습니다! (이 예에서는 만약 i0으로 바꾸어 -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 쉘에서 다음과 같은 오류가 발생하는 것입니다.

  • 이것은 대화 형 쉘의 기본 설정에서는 Bashcsh-style 명령 히스토리 확장을 !을 이용하여 사용하기 때문에 발생합니다. 이것은 쉘 스크립트의 문제가 아니라 대화 형 쉘 만에서 발생하는 문제입니다.

  • 이 현상에 대한 해결법은 아래와 같습니다.

$ echo "hi\!"

hi\!

 

  • 가장 간단한 해결 방법은 histexpand옵션을 설정하지 않는 것입니다. 이것은 set +Hset +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

  • 물결표 열기 (사용자 이름과 함께 또는 사용자 이름없이) 물결표가 문자열의 처음 또는 슬래시 후, 또는 물결표 만 때만 발생하는 것이 보증되고 있습니다.

  • 또한 =의한 변수에 대입 직후 물결이 나타나는 경우도 보증되고 있습니다.

  • 그러나 exportlocal명령은 이러한 대입을 구성하지 않습니다.

  • , 어떤 쉘 (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편

 

[Shell Script] 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (1)

정보 업무명 : 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (1) 작성자 : 박진만 작성일 : 2020-03-21 설 명 : 수정이력 : 요약 [특징] 쉘 스크립트 개발자가 빠지기 쉬운 함정을 소개. [활용 자료] 없음 [자료..

shlee1990.tistory.com

 

  • 2편

 

[Shell Script] 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (2)

정보 업무명 : 쉘 스크립트 (bash) 개발자가 빠지기 쉬운 함정 (2) 작성자 : 박진만 작성일 : 2020-03-22 설 명 : 수정이력 : 요약 [특징] 쉘 스크립트 개발자가 빠지기 쉬운 함정을 소개. [활용 자료] 없음 [자료..

shlee1990.tistory.com

 

 참고 문헌

[논문]

  • 없음

[보고서]

  • 없음

[URL]

  • 없음

 

 문의사항

[기상학/프로그래밍 언어]

  • sangho.lee.1990@gmail.com

[해양학/천문학/빅데이터]

  • saimang0804@gmail.com

 

 

 

 

 

 

 

 

 

 

 

본 블로그는 파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음