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
IFS 값의 설정
sh 에는 $' '
이 없습니다. 그러므로 IFS 값을 변경하려면 다음과 같이 합니다.
# IFS 값을 newline 으로 설정
sh$ IFS='
'
# IFS 값을 기본값으로 설정
# $( ) 을 이용해 변수에 값을 대입할때는 마지막 newline 이 제거되기 때문에 \t 을 \n 뒤에 두어야 합니다
sh$ IFS=$(echo " \n\t")
$(( . . . ))
이 특수 표현식은 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 함수를 사용할 때와 같이 process 가 생성되지 않는 경우는 변수에 값이 대입돼버리고 이전 값으로 복귀가 안됩니다.
# 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
# script 파일을 실행하는 것처럼 process 가 생성되는 경우는 정상작동합니다.
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; IFS=`echo " \n\t"`
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
[[ . . . ]] , (( . . . ))
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=$(expr `od -An -N1 -tu1 /dev/urandom` % 100)
$ 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
$ random=$(shuf -n1 -i 1-99)
$ random=$(cat <(seq 10 20) <(seq 80 90) | shuf -n1)
$ random=$(shuf -n1 -e a b c d e)
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 이해하기
이것은 bash 에서 echo -e
를 사용한 것과 동일한 결과를 같습니다.
sh$ echo X\tX # no quotes
XtX
sh$ echo 'X\tX' # single quotes
X X
sh$ echo "X\tX" # double quotes
X X
sh$ echo X\\tX # no quotes 에서는 기본적으로 모든 문자가 escape 된다.
X X
sh$ echo 'X\\tX'
X\tX
sh$ echo "X\\tX" # double quotes 에서는 기본적으로
X X # ( " ),( $ ),( ` ),( \ ),( newline ) 문자가 escape 된다.
sh$ echo X\\\tX
X X
sh$ echo 'X\\\tX'
X\ X
sh$ echo "X\\\tX"
X\tX
sh$ echo X\\\\tX
X\tX
sh$ echo 'X\\\\tX'
X\\tX
sh$ echo "X\\\\tX"
X\tX
sh$ echo X\\\\\tX
X\tX
sh$ echo 'X\\\\\tX'
X\\ X
sh$ echo "X\\\\\tX"
X\ X