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 &> filecommand > file 2>&1 로 변경합니다.

command1 |& command2command1 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