14장. 명령어 치환(Command Substitution)

명령어 치환은 하나나 그 이상의 명령어의 출력을 재할당 해줍니다. 명령어 치환은 말그대로 한 명령어의 출력을 다른 문맥으로 연결해 줍니다.

명령어 치환의 전형적인 형태는 역따옴표(`...`)를 쓰는 것입니다. 역따옴표 안에 들어 있는 명령어는 명령어 줄에서 쓸 수 있는 텍스트를 만들어 냅니다.
script_name=`basename $0`
echo "이 스크립트의 이름은 $script_name 입니다."

명령어의 출력은 다른 명령어의 인자로 쓸 수 있는데, 변수를 설정하거나 for 루프에서 인자 리스트로도 쓸 수 있습니다.

rm `cat filename`   # "filename" 은 지울 파일 목록을 갖고 있습니다.
#
# S. C. 가 "arg list too long" 이란 에러가 나올 수도 있다고 지적했습니다.
# 더 좋은 방법             xargs rm -- < filename
# ( -- 는 "-"로 시작하는 "filename"도 처리해 줍니다. )

textfile_listing=`ls *.txt`
# 현재 디렉토리의 모든 *.txt 파일의 이름을 담고 있는 변수.
echo $textfile_listing

textfile_listing2=$(ls *.txt)   # 명령어 치환의 다른 형태.
echo $textfile_listing
# 똑같은 결과.

# 파일 목록을 하나의 문자열로 가져가면 뉴라인 문자가 중간에 들어가는
# 문제가 생길 수도 있습니다.
#
# 파일 목록을 인자로 지정하는 안전한 방법은 배열을 사용하는 것입니다.
#      shopt -s nullglob    # 일치하는게 없다면 파일명 확장은 무의미해 집니다.
#      textfile_listing=( *.txt )
#
# Thanks, S.C.

경고

명령어 치환은 낱말 조각남(word splitting)이 생길수도 있습니다.
COMMAND `echo a b`     # 2개의 인자: a 와 b

COMMAND "`echo a b`"   # 1개의 인자: "a b"

COMMAND `echo`         # 인자 없음

COMMAND "`echo`"       # 하나짜리 빈 인자


# Thanks, S.C.

경고

명령어 치환에서 낱말 조각남은 재할당되는 명령어의 출력에서 뉴라인 문자들을 지워 버려서 유쾌하지 않은 결과를 가져 올 수 있습니다.
dir_listing=`ls -l`
echo $dirlisting

# 다음처럼 멋지게 정렬된 디렉토리 목록을 바라겠지만
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh

# 실제로 여러분은 이런 결과를 보게 됩니다.:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# 뉴라인 문자가 사라져 버렸습니다.

명령어 치환에서 낱말 조각남이 안 생긴다 하더라도 뉴라인 문자를 지워버릴 수 있습니다.
# cd "`pwd`"  # 이렇게 하면 항상 동작할 겁니다.
# 하지만...

mkdir '제일 끝에 뉴라인이 있는 디렉토리
'

cd '제일 끝에 뉴라인이 있는 디렉토리
'

cd "`pwd`"  # 에러 메세지:
# bash: cd: /tmp/file with trailing newline: No such file or directory

cd "$PWD"   # 잘 됩니다.





old_tty_setting=$(stty -g)   # 현재의 터미널 세팅을 저장.
echo "키를 누르세요 "
stty -icanon -echo           # 터미널의 "캐노니컬"(canonical) 모드를 끄고,
                             # "로컬" 에코도 끔.
key=$(dd bs=1 count=1 2> /dev/null)   # 키누름을 얻기 위해 'dd'를 씀.
stty "$old_tty_setting"      # 저장해 놓았던 세팅을 복구.
echo "${#key} 개의 키를 눌렀습니다."  # ${#variable} = $variable 에 들어 있는 문자수
#
# RETURN 말고 다른 키를 눌렀을 때의 출력은 "1 개의 키를 눌렀습니다."
# RETURN 을 눌렀을 때의 출력은 "0 개의 출력을 눌렀습니다."
# 명령어 치환이 뉴라인 문자를 먹어 버렸습니다.

Thanks, S.C.

작은 정보: 명령어 치환은 재지향을 써서 파일의 내용을 변수로 세팅하거나 cat 명령어를 써서 할 수 있게 해줍니다.
variable1=`<file1`      # "variable1" 을 "file1"의 내용으로 세트.
variable2=`cat file2`   # "variable2" 를 "file2"의 내용으로 세트.

#  변수에 공백문자가 포함된 값이 들어갈수도 있고
#+ 심지어는 제어 문자가 들어갈수도 있기 때문에 주의해야 합니다.

참고: 이제는 역따옴표 대신 $(COMMAND) 형태의 명령어 치환이 쓰입니다.
output=$(sed -n /"$1"/p $file)
# "grp.sh" 예제에서.

쉘 스크립트에서 명령어 치환이 쓰이는 예제들:

  1. 예 10-7

  2. 예 10-24

  3. 예 9-21

  4. 예 12-2

  5. 예 12-15

  6. 예 12-12

  7. 예 12-31

  8. 예 10-12

  9. 예 10-9

  10. 예 12-24

  11. 예 16-5

  12. 예 A-12

  13. 예 28-1

  14. 예 12-28

  15. 예 12-29

  16. 예 12-30