Dash
Bash
가 interactive shell 이라면 sh
은 script 용 shell 이라고 할 수 있을 정도로
sh 은 bash 에 비해 많은 기능이 빠져있고 script 작성에 필요한 최소한의 기능만을 가지고 있습니다.
sh 은 POSIX 에서 규정한 일종의 specification 으로 이를 구현한 구현체 중의 하나가 dash
(debian almquist shell) 입니다.
sh 은 POSIX 시스템에 기본적으로 포함되므로 각기 다른 shell 환경을 가지는 여러 종류의 OS 에서
호환성의 문제없이 실행될 수 있습니다.
그러므로 배포용 스크립트를 작성할 때는 주로 sh 을 사용합니다.
sh 은 bash 에 비해 실행파일의 크기도 작고 실행 속도도 빨라서 빠른 start-up time 을 요구하는 boot script 을 작성할 때나 특정 프로그램에서 shell 을 이용해 외부 명령을 실행시킬 때 bash 보다는 sh 을 사용합니다.
http://unix.stackexchange.com/a/148061/117612 에서 bash 와 비교한 것을 볼 수 있습니다.
sh 로 작성한 스크립트가 실행될 때는 bash 에서 export 한 변수는 사용할 수 있지만
함수는 사용할 수 없습니다.
( 따라서 원본 명령 실행을 위해 command 명령을 사용할 필요가 없습니다 ).
sh 은 script 를 작성하는데 필요한 기본적인 기능만 제공합니다.
그러므로 [[ ]]
, (( ))
, let
과 같은 bash 에서만 제공하는 특수 명령은 사용할 수 없습니다. ( 보통 bashism 이라고 합니다 )
echo
sh 에서 echo 명령은 bash 에서 echo -e
와 같습니다.
그러므로 기본적으로 escape 문자가 처리됩니다.
sh$ echo -n ' \n\t' | od -a
0000000 sp nl ht
escape 문자가 처리되지 않게하려면 printf "%s\n" ...
를 사용하면 됩니다.
자세한 내용은 sh quotes 이해하기 를 참조하세요
8 진수 형식의 사용
sh 에서는 echo 와 printf 문에서 16 진수 형식을 사용할 수 없습니다.
자세한 사항은 escape sequences 메뉴를 참고하세요
# sh 은 8 진수 형식만 사용가능
sh$ echo '\101\102\103'
ABC
sh$ echo '\0101\0102\0103'
ABC
sh$ echo '\x41\x42\x43' # \x<HH> 16 진수 형식 안됨
\x41\x42\x43
test 명령에서 스트링 equal 연산자
sh 의 builtin 명령인 test 의 경우 bash 와달리 스트링 equal 연산자로 ==
를 사용할 수 없습니다.=
를 사용해야 합니다. ( !=
은 동일 )
bash$ [ "foo" == "foo" ]; echo $? # bash 에서는 사용가능
0
$ /usr/bin/test "foo" == "foo"; echo $? # /usr/bin/test 외부 명령도 사용가능
0
sh$ [ "foo" == "foo" ]; echo $? # sh 에서는 오류
sh: 1: test: foo: unexpected operator
2
sh$ [ "foo" = "foo" ]; echo $? # sh 에서는 '=' 연산자를 사용해야 한다.
0
IFS 값의 설정
sh 에는 $' '
이 없습니다. 그러므로 IFS 값을 변경하려면 다음과 같이 합니다.
# IFS 값을 newline 으로 설정
sh$ IFS='
'
# IFS 값을 기본값으로 설정
# $( ) 을 이용해 변수에 값을 대입할때는 마지막 newline 이 제거되기 때문에 \t 을 \n 뒤에 두어야 합니다
sh$ IFS=$(echo " \n\t")
# 또는
sh$ unset IFS
$(( . . . ))
산술 확장은 sh 에서 사용 가능하지만 다음 연산자들은 사용할 수 없습니다.
++
, --
연산자
sh$ : $(( i++ ))
sh: 2: arithmetic expression: expecting primary: " i++ " # 오류발생
# 다음과 같이 수정해 사용합니다.
i=$((i+1)) 또는 i=$((i-1))
: $((i+=1)) 또는 : $((i-=1))
,
연산자
sh$ : $(( i+=1, i+=1 ))
sh: 21: arithmetic expression: expecting EOF: " i+=1 , i+=1 " # 오류발생
**
연산자
sh$ echo $(( 2**3 ))
sh: 363: arithmetic expression: expecting primary: " 2**3 " # 오류발생
Parameter expansion
sh 에서 다음 기능은 사용할 수 없습니다.
Substring expansion : ${parameter:offset:length} ...
Search and replace : ${parameter/pattern/string} ...
Indirection : ${!parameter}
Case modification : ${parameter^^}, ${parameter,,} ...
Redirections
1>&3-
는 두 단계로 나누어서 합니다. 1>&3 3>&-
command &> file
는 command > file 2>&1
로 변경합니다.
command1 |& command2
는 command1 2>&1 | command2
로 변경합니다.
read
-r
옵션만 사용가능 합니다.
FD 번호
sh 에서는 redirection 이나 exec 에서 사용할 수 있는 FD 번호가 single-digit file descriptors 로 제한됩니다. ( 0 ~ 9 번까지 ) 그리고 named file descriptor 는 사용할 수 없습니다.
command history 기능
sh 에서는 command history 기능을 제공하지 않습니다.
그러므로 프롬프트 상에서 history 확장을 금지하기 위해 !
문자를 escape 할 필요가 없습니다.
export
sh 에서는 변수만 export 할 수 있고 function 은 export 할 수 없습니다.
따라서 bash 에서 export -f 한 함수는 sh 에서 사용할 수 없습니다.
명령에 선행하는 대입 연산
read 명령에서는 bash 와 동일하게 동작하나 그외 eval 이나 shell 함수를 사용할 때와 같이 프로세스가 생성되지 않는 경우는 변수에 값이 대입돼버리고 이전 값으로 복귀가 안됩니다.
# read 명령에서는 bash 와 동일하게 동작
sh$ echo -n "$IFS" | od -a
0000000 sp ht nl
sh$ IFS=: read v1 v2 v3 # [enter]
1:2:3 # 입력
sh$ echo $v1 $v2 $v3
1 2 3
sh$ echo -n "$IFS" | od -a # 원래 값으로 복귀
0000000 sp ht nl
# eval 을 사용할 경우
sh$ a=1 b=2 c=3
sh$ a=4 b=5 c=6 eval 'echo $a $b $c'
4 5 6
sh$ echo $a $b $c # 원래 값으로 복귀가 안되고 변수에 값이 대입됨
4 5 6
# 스크립트 파일을 실행하는 것처럼 프로세스가 생성되는 경우는 정상작동합니다.
sh$ echo $a $b $c
1 2 3
sh$ a=4 b=5 c=6 sh -c 'echo $a $b $c'
4 5 6
sh$ echo $a $b $c
1 2 3
Bashism
다음에 이어지는 내용은 bash 에서만 사용할 수 있는 기능으로 sh 에서는 사용할 수 없습니다.
test 명령에서 '==' 의 사용
[ "$var1" == "$var2" ] # bashism 으로 사용할 수 없음
[ "$var1" = "$var2" ] # '=' 로 수정
+=
+=
대입 메타문자는 사용할 수 없습니다. 대신에 다음과 같은 식으로 사용하면 됩니다.
### bash ###
bash$ AA=hello
bash$ AA+=" World"
bash$ echo "$AA"
hello World
### sh ###
sh$ AA=hello
sh$ AA="$AA World" # 또는 "$AA"" World"
sh$ echo "$AA"
hello World
$' ' , $" "
bashism 으로 사용할 수 없습니다.
Array
Array 는 POSIX 에 정의되어있지 않으므로 indexed array, associative array 모두
사용할 수 없고 AA=()
표현식도 사용할 수 없습니다.
대신에 다음과 같이 positional parameters 를 활용할 수 있습니다.
#!/bin/sh
line="11:22:33:44:55"
set -f; IFS=: # globbing 을 disable
set -- $line # IFS 값에 따라 필드를 분리하여 positional parameters 에 할당
set +f; unset IFS
echo number of fields = $#
echo field 1 = "$1"
echo field 2 = "$2"
shift 3
echo \$@ = "$@"
############# output #############
number of fields = 5
field 1 = 11
field 2 = 22
$@ = 44 55
$* 와 $@
sh 에서는 $@
값을 변수에 대입할 경우 $*
와 동일하게 IFS
변수의 첫번째 문자가
인수의 구분자로 사용됩니다.
# bash 의 경우 # sh 의 경우
$ set -- 11 22 33 44 55 $ set -- 11 22 33 44 55
$ IFS=XYZ $ IFS=XYZ
$ foo=$* $ foo=$*
$ echo "$foo" , $foo $ echo "$foo" , $foo
11X22X33X44X55 , 11 22 33 44 55 11X22X33X44X55 , 11 22 33 44 55
$ bar=$@ $ bar=$@
$ echo "$bar" , $bar $ echo "$bar" , $bar
11 22 33 44 55 , 11 22 33 44 55 11X22X33X44X55 , 11 22 33 44 55
[[ . . . ]] , (( . . . ))
for ((i=0; i<3; i++))...
bashism 으로 모두 사용할 수 없습니다.
Brace expansion
사용할 수 없습니다.
Process substitution
사용할 수 없습니다.
<<<
here string 은 사용할 수 없습니다.
function
function myfunc() { ... ;} # function 키워드는 사용할 수 없음
myfunc() { ... ;} # 수정
source
source ./subscript.sh # source 명령은 사용할 수 없음
. ./subscript.sh # '.' 명령으로 수정
declare
declare 는 사용할 수 없지만 함수 내에서 local 은 사용할 수 있습니다.
select, disown
사용할 수 없습니다.
shopt
shopt 명령을 이용한 shell 옵션 설정은 bash 전용으로 sh 에서는 사용할 수 없습니다.
$LINENO, $PIPESTATUS
사용할 수 없습니다.
$RANDOM
# $RANDOM 변수는 사용할 수 없으므로 다음과 같은 방법을 이용합니다.
$ random=$(( `od -An -N1 -tu1 /dev/urandom` % 100 ))
$ random=$(shuf -n1 -i 1-99)
$ random=$(cat <(seq 10 20) <(seq 80 90) | shuf -n1) # 10~20 80~90 사이 수
$ random=$(shuf -n1 -e a b c d e)
$ shuf -i 100-200 | awk '{ printf "%4d,", $1 } NR % 10 == 0 {print ""}'
189, 135, 110, 119, 149, 137, 194, 105, 195, 193,
102, 192, 122, 157, 183, 198, 141, 120, 108, 175,
127, 124, 138, 173, 174, 123, 176, 190, 134, 104,
. . .
$ awk 'BEGIN { srand(); for (i=0; i<100; i++) print int(rand()*100) }' |
xargs -n20 | column -t
34 63 65 37 14 97 62 55 52 88 42 50 72 40 60 24 52 18 95 65
77 72 71 35 54 57 31 24 81 66 78 18 73 5 69 24 49 83 11 4
70 13 1 50 65 88 45 10 16 56 19 78 66 62 60 3 73 87 24 44
56 83 16 51 15 80 17 32 29 92 47 0 88 31 81 53 94 55 79 31
50 46 12 89 61 33 68 22 80 12 36 17 84 62 80 54 60 29 54 3
bash 스크립트를 sh 스크립트로 변경할 때 고려해야 될 것
echo ...
를printf "%s\n" ...
로 변경echo -n ...
를printf "%s" ...
로 변경echo -e ...
를echo ...
로 변경
sh 스크립트를 bash 스크립트로 변경할 때 고려해야 될 것
sh
에서 echo 명령은 기본적으로 bash 에서 echo -e
와 같으므로
echo ...
를echo -e ...
로 변경
sh quotes 이해하기
sh 에서 echo 명령은 bash 에서 echo -e
를 사용한 것과 동일한 결과를 갖습니다.
따라서 quotes 을 해석할 때는 먼저 bash 에서 동일한 명령을 실행해 보고
나온 결과에 대해서 escape sequence 를 적용하면 됩니다.
sh$ echo "11\\t22\\t33" # sh 에서의 quotes 처리를 해석하려면
11 22 33
bash$ echo "11\\t22\\t33" # bash 에서 동일한 명령을 실행해서 결과를 출력해보고
11\t22\t33 # 출력 결과에 대해서 escape sequence 를 적용하면 된다.
# < No quotes > < Double quotes > < Single quotes >
$ echo 11\t22\t33 $ echo "11\t22\t33" $ echo '11\t22\t33'
11t22t33 11 22 33 11 22 33
$ echo 11\\t22\\t33 $ echo "11\\t22\\t33" $ echo '11\\t22\\t33'
11 22 33 11 22 33 11\t22\t33
$ echo 11\\\t22\\\t33 $ echo "11\\\t22\\\t33" $ echo '11\\\t22\\\t33'
11 22 33 11\t22\t33 11\ 22\ 33
$ echo 11\\\\t22\\\\t33 $ echo "11\\\\t22\\\\t33" $ echo '11\\\\t22\\\\t33'
11\t22\t33 11\t22\t33 11\\t22\\t33
$ echo 11\\\\\t22\\\\\t33 $ echo "11\\\\\t22\\\\\t33" $ echo '11\\\\\t22\\\\\t33'
11\t22\t33 11\ 22\ 33 11\\ 22\\ 33