명령 의 구분

1. 외부 명령

/usr/bin/find 명령과 같이 시스템 디렉토리에 위치한 명령들입니다. 여기에는 ELF 바이너리 실행파일뿐만 아니라 shell 스크립트, perl 스크립트, python 스크립트와 같은 텍스트 실행파일도 많이 포함되어 있습니다. GNU coreutils 패키지 에는 textutils, shellutils, fileutils 명령들이 포함되어 있습니다.

# 텍스트 형태의 실행파일 개수
$ file /bin/* /usr/bin/* | grep -ic "text executable"
643

$ file /bin/* /usr/bin/* | grep -ic "shell script"
324

$ file /bin/* /usr/bin/* | grep -ic "perl script"
214

$ file /bin/* /usr/bin/* | grep -ic "python script"
97

$ file /bin/* /usr/bin/* | grep -i "shell script"
/bin/bzdiff:                                  POSIX shell script, ASCII text executable
/bin/bzexe:                                   POSIX shell script, ASCII text executable
/bin/bzgrep:                                  POSIX shell script, ASCII text executable
/bin/bzmore:                                  POSIX shell script, ASCII text executable
/bin/egrep:                                   POSIX shell script, ASCII text executable
/bin/fgrep:                                   POSIX shell script, ASCII text executable
/bin/gunzip:                                  POSIX shell script, ASCII text executable
...
...

2. shell builtins

Shell 에 builtin 돼서 제공되는 명령들로 실행을 위해 새로 프로세스를 만들지 않아도 되기 때문에 외부 명령에 비해 좀더 효율적으로 실행될수 있고 shell 내부 상태정보를 조회, 변경할 수 있습니다.

$ compgen -b | column

.         :         [         alias     bg        bind      break     
builtin   caller    cd        command   compgen   complete  compopt   
continue  declare   dirs      disown    echo      enable    eval      
exec      exit      export    false     fc        fg        getopts   
hash      help      history   jobs      kill      let       local     
logout    mapfile   popd      printf    pushd     pwd       read      
readarray readonly  return    set       shift     shopt     source    
suspend   test      times     trap      true      type      typeset   
ulimit    umask     unalias   unset     wait

echo, printf, test, [, true, false, kill, pwd 같은 명령들은 동일한 기능을 하는 외부 명령이 중복되어 존재하는데 이것은 shell 환경이 아닌 경우에도 사용할 수 있게 하기 위해서입니다.

# read 명령은 shell builtin 으로 env 명령으로 실행할 수 없다.
$ env read var
env: ‘read’: No such file or directory

# test, echo 같은 명령들은 shell builtin 이지만 외부 명령으로도 존재하므로 env 로 실행할 수 있다.
$ env test 1 == 2; echo $?
1

$ sudo echo 123
123

기본적으로 bash 에서 제공되는 builtin 명령 외에 사용자가 직접 명령을 만들어서 추가할 수도 있습니다. bash-builtins 패키지를 설치하면 추가해서 사용할 수 있는 명령들과 예제를 볼 수 있습니다. ( 이때는 enable 명령이 사용됩니다. )

3. shell keywords

Shell keywords 는 compound commands 작성시 사용됩니다. 일반 명령과 같이 명령문의 처음에 와야 키워드로 동작하고 그외 명령문의 중간에 올경우는 키워드로 인식되지 않고 단순 명령문 스트링이 됩니다. [[ ]] 의 경우는 키워드 이므로 [ ] 명령과 달리 직접 shell 에의해 해석이되어 실행 되고 ! 는 logical NOT 기능을 합니다.

$ compgen -k | column

if        then      else      elif      fi        case      
esac      for       select    while     until     do        
done      in        function  time      {         }         
!         [[        ]]        coproc

4. shell functions

사용자가 임의로 원하는 함수를 만들어 사용할 수 있습니다. 함수명은 외부 명령, builtin 명령과 동일하게 사용됩니다. declare -F 명령을 이용하면 현재 shell 에 설정돼있는 함수들을 볼 수 있습니다.

5. aliases

앞서 소개한 명령들을 alias 하여 사용할수 있습니다. shell 키워드도 alias 해서 사용할 수 있을 정도로 우선순위가 제일 높습니다. non-interactive shell 인 스크립트 파일 실행 시에는 기본적으로 disable 됩니다.


여담으로 shell 에서 기본적으로 사용되는 ls, mv, cp, rm 같은 명령들을 보면 이름이 굉장히 짧은데 이것은 unix 초기에는 지금처럼 키보드 성능이 좋지 못해서 하루 종일 일하고 나면 손가락 관절에 무리가 올 정도였다고 합니다. 따라서 한번이라도 키를 덜 누르는 것이 중요했기 때문에 명령 이름이 짧아졌다고 합니다.

명령 이름이 중복되어 나타날때

명령 이름이 앞서 분류한 곳에 중복되어 나타나는 경우가 있습니다. 관련된 정보를 보려면 builtin 명령인 type 을 이용합니다. type -a 명령 은 우선순위가 높은 명령부터 차례로 표시해줍니다.

$ type -a kill
kill is a shell builtin
kill is /bin/kill

$ type -a time
time is a shell keyword
time is /usr/bin/time

$ type -a [
[ is a shell builtin
[ is /usr/bin/[

명령을 찾는 우선순위는 alias, keyword, function, builtin, 외부 명령 순 입니다. kill 명령은 builtin 에도 있고 외부 명령에도 있는데 같은 이름의 function 을 새로 만든다면 function 이 실행되게 됩니다.

이렇게 중복되는 이름 문제를 해결하기 위해 shell 에서는 다음과 같은 명령들을 제공합니다.

  • command : 우선순위가 높은 alias, keyword, function 이름을 피해 외부명령, builtin 명령을 실행합니다.
    외부, builtin 명령의 기능을 확장하기 위해 동일한 이름의 함수를 만들어 사용할땐 함수안에서 command 명령 을 사용해야 원본 명령을 실행할 수 있습니다. 또한 배포를 위한 shell script 작성시 명령 앞에는 command 를 붙여야 원본 명령이 실행되는 것을 보장할 수 있습니다.

  • builtin : 우선순위가 높은 alias, function 이름을 피해 builtin 명령을 실행하기 위해 사용합니다.

  • enable : builtin 명령을 disable 하여 외부 명령을 실행하게 할 수 있습니다.

alias , keyword 의 escape

alias 나 keyword 는 특별한 기능을 하는 단어로 볼 수 있습니다. 우선순위 또한 일반 명령들 보다 높은데 다음과 같은 방법을 이용하면 해당 기능을 disable 할 수 있습니다.

  • alias, keyword 이름 앞에 \ 문자를 붙인다.

  • alias, keyword 이름을 quote 한다

time 은 keyword 이면서 /usr/bin/time 외부 명령이기도 한데 다음과 같이 하면 키워드 기능이 escape 되어 외부 명령을 실행할 수 있습니다.

$ \time
$ 'time'      # 또는 "time"

ls 명령이 alias 되어 있다면 다음 명령으로 alias 기능을 escape 하여 원본 명령을 실행할 수 있습니다.

$ \ls
$ 'ls'       # 또는 "ls"

function 이나 builtin 명령은 위 방법으로 escape 할 수 없습니다. builtin 명령과 중복되는 외부 명령이 있다면 전체 경로를 입력하여 실행하거나 enable 명령으로 builtin 명령을 disable 할 수 있습니다. function 의 경우는 command 명령 으로 외부명령을 실행할 수 있습니다.

명령들의 차이점

예제 .1

다음은 [ 명령과 [[ 키워드의 사용상 차이점입니다.

똑같은 문장인데 [ 명령 에서는 오류가 나고 [[ 키워드 에서는 정상적으로 실행이 됩니다. [ 는 앞서 살펴본 바와같이 builtin 명령으로 일반 명령들과 사용법이 같습니다. command arg1 arg2.. 이런식이죠. 그러므로 [ -n $AA ] 명령문이 변수확장이 되면 [ -n hello world ] 와 같게 됩니다. 결과적으로 hello 를 binary 연산자로 -nworld 를 피연산자로 해석을 해서 hello 라는 연산자가 없다는 오류가 납니다. 반면에 [[ 키워드는 직접 shell 이 표현식을 해석하여 실행하므로 $AA 값을 올바르게 인식합니다.

$ AA="hello world"

$ if [ -n $AA ]; then
>       echo "$AA"
> fi
bash: [: hello: binary operator expected

----------------------------------------

$ if [[ -n $AA ]]; then
>       echo "$AA"
> fi
hello world

예제 .2

다음 예로 일반 명령과 키워드의 실제 사용되는 시점에 대해 알 수 있습니다.

$a 변수확장이 일어나고 [ -d /tmp ] 명령문이 실행되어 정상적인 결과값이 나왔습니다.

$ a='['
$ $a -d /tmp ]
$ echo $?
0

$a 변수확장이 일어나고 [[ -d /tmp ]] 명령문이 실행되었으나 오류가 발생했습니다. 이 결과로 알 수 있는 것은 키워드 해석 작업은 변수확장 보다 우선해서 처리된다는 것입니다. 키워드 해석 작업이 이미 끝난 상태이므로 [[ 명령이 없다는 오류가 발생했습니다.

$ a='[['
$ $a -d /tmp ]]
bash: [[: command not found

예제 .3

time 명령은 shell 키워드이고 /usr/bin/time 명령이기도 합니다. 먼저 키워드를 이용해 다음 문장을 실행해보면 오류 없이 정상적으로 실행이 됩니다.

$ time { sleep 1 ;}

real    0m1.003s
user    0m0.000s
sys     0m0.003s

이번에는 \ 를 이용해 키워드를 escape 해서 /usr/bin/time 명령으로 실행해보면 외부 명령으로는 구문 에러가 나는 것을 알 수 있습니다.

$ \time { sleep 1 ;}
bash: syntax error near unexpected token `}'

$ \time { sleep 1 }
time: cannot run {: No such file or directory
. . .
. . .

script file or function or alias ?

shell 에서 명령을 작성할 때 외부 명령처럼 script file 로 작성할지 아니면 shell function 으로 작성할지 아니면 alias 로 할지 애매한 경우가 있는데요. 차이점을 살펴보면 다음과 같습니다.

  • 일단 가장 큰 차이는 function 이나 alias 는 외부에 파일로 존재하지 않기 때문에 특정 명령이 실행 중에 외부 명령으로 실행시키고자 할때 사용할 수 없고, 또한 sudo, env, xargs 명령이나 find 명령의 -exec 액션에서도 사용할 수 없습니다. 여기서 사용할 수 있으려면 외부에 파일로 존재하는 script file 로 작성해야 합니다.

  • script file 작성 시에는 export -f 한 shell function 을 사용할 수 있고, shell function 에서는 alias 정의를 사용할 수 있습니다. 하지만 script file 에서 alias 는 직접 사용할 수 없습니다.

function 을 export 하는 기능은 bash 에서만 가능하고 sh 에서는 안됩니다. 예를 들어 vi 에디터에서는 외부 명령을 실행할때 sh 이 아닌 bash 를 사용하므로 export 한 function 을 실행시킬 수 있지만 alias 는 사용할 수 없습니다.

  • alias, function 은 현재 프롬프트 shell 에서 사용되므로 shell 의 환경 변수값을 바꾸거나 옵션 설정을 변경할 수 있습니다. 하지만 script file 은 bash child process 가 새로 생성되어 실행되므로 현재 shell 환경을 변경할 수 없습니다.

예를 들어 builtin 명령인 cd 명령을 확장해 사용하려 할 경우 function 으로만 가능합니다. [참조]

  • alias 는 \ 로 간단하게 escape 할수 있는 반면에 function 의 경우는 command 명령을 사용해야 합니다.

Command line 개념

find 명령의 -exec 액션이나, xargs, 또는 sudo 같은 명령들은 자체 명령도 실행되지만 입력받은 child 명령도 실행하죠. 그럼 여기서 파이프나 redirection 을 사용하게 되면 자체 명령에 적용해야 하는지 아니면 child 명령에 적용해야 하는지 문제가 생깁니다.

다음 명령을 보면 *.c 파일을 선택해서 grep 한 결과를 *.c.out 파일에 각각 저장하려고 한 것인데요. 그러니까 foo.c bar.c zoo.c 파일이 있을 경우 foo.c.out bar.c.out zoo.c.out 가 생성돼야 하는데 실행을 해보면 grep 한 결과가 {}.out 파일 하나에 모두 저장되는 것을 볼 수 있습니다. 이렇게 되는 이유는 현재 command line 이 find 와 xargs 에 해당하기 때문에 > redirection 이 grep 명령에 적용되지 않고 find 와 xargs 에 적용되기 때문입니다.

$ find -name '*.c' -exec grep ABC {} > {}.out \;

$ ls
bar.c  foo.c  zoo.c  {}.out

$ ls *.c | xargs -i grep ABC {} > {}.out

$ ls
bar.c  foo.c  zoo.c  {}.out

# 이해를 돕기 위해 위 명령문을 다시 작성해 보면 아래와 동일한 명령문이 됩니다.
# 다시 말해서 '>' redirection 이 find 와 xargs 명령에 해당되는 것입니다.

$ > {}.out find -name '*.c' -exec grep ABC {} \;

$ ls *.c | > {}.out xargs -i grep ABC {}

그러면 원래 의도한 대로 > redirection 이 grep 명령의 command line 에 적용되게 하려면 어떻게 해야 될까요? sh -c or bash -c 형식을 이용하여 다음과 같이 grep 명령의 command line 을 만들어 주어야 합니다.

$ find -name '*.c' -exec sh -c 'grep ABC "{}" > "{}".out' \;

$ ls *.c | xargs -i sh -c 'grep ABC "{}" > "{}".out'

$ ls
bar.c  bar.c.out  foo.c  foo.c.out  zoo.c  zoo.c.out

이것은 다음과 같이 파이프를 이용해 명령을 연결할 때도 동일하게 적용됩니다. 마찬가지로 foo.c bar.c zoo.c 각각의 파일에서 grep 한결과를 sort 해서 제일 위의 라인 하나만 출력하려고 한 것인데요. 그러니까 파일이 3 개 이므로 3 개의 라인이 출력돼야 합니다. 하지만 다음과 같은 방법으로는 오류가 발생하거나 의도한 대로 결과가 출력되지 않습니다.

# find 명령의 command line 은 sort 명령 앞에 있는 파이프까지인데
# find 명령의 인수에 해당하는 '\;' 가 문장 끝에 위치하므로 오류가 됩니다.

$ find -name '*.c' -exec grep ABC {} | sort | head -1 \;
head: cannot open ';' for reading: No such file or directory
find: missing argument to '-exec'
Try 'find --help' for more information.

# 따라서 다음과 같이 하게 되면 결과적으로 하나의 라인만 출력이 되겠죠.
$ find -name '*.c' -exec grep ABC {} \; | sort | head -1
line ABC ...

foo.c bar.c zoo.c 각각의 파일에서 sort 한 결과 첫 라인이 출력되게 하려면 다음과 같이 sh -c 을 이용하여 grep 명령의 command line 을 만들어 주어야 합니다.

$ find -name '*.c' -exec sh -c 'grep ABC "{}" | sort | head -1' \;
line ABC....              # from foo.c
line ABC....              # from bar.c
line ABC....              # from zoo.c

$ ls *.c | xargs -i sh -c 'grep ABC "{}" | sort | head -1'
이번에는 sudo 명령을 살펴보겠습니다.

/proc/sys/kernel/yama/ptrace_scope 는 strace 명령과 관련된 커널 옵션인데요. 아래를 보면 퍼미션이 -rw-r--r-- 로 되어있어서 일반 사용자가 값을 읽을 수는 있어도 쓸 수는 없습니다. 그래서 값을 변경하기 위해 sudo 명령을 사용하고 있는데 Perminssion denied 가 발생하는 것을 볼 수 있습니다.

$ ls -l /proc/sys/kernel/yama/ptrace_scope
-rw-r--r-- 1 root root 0 2017-01-25 02:34 /proc/sys/kernel/yama/ptrace_scope

$ cat /proc/sys/kernel/yama/ptrace_scope
1

$ sudo echo 0 > /proc/sys/kernel/yama/ptrace_scope
bash: /proc/sys/kernel/yama/ptrace_scope: Permission denied

위 명령에서 > 는 sudo 하고 echo 명령 중에 어느 명령에 해당될까요? 현재 sudo 명령의 command line 이므로 > 는 echo 명령이 아닌 sudo 명령에 해당됩니다. sudo 명령은 일반 유저에 의해 실행되죠. 그러므로 > 도 역시 일반 유저 권한으로 처리되어 permission 에러가 발생합니다.

따라서 sudo 명령에 의해 echo 명령과 > 가 root 권한으로 실행되려면 다음과 같이 sh -c 를 이용해 echo 명령의 command line 을 만들어 주어야 합니다.

$ sudo sh -c 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'

다음을 보면 sudo 명령의 command line 에 해당되는 > x2 파일은 일반 유저로 나오고 echo 명령의 command line 에 해당되는 > x1 파일은 root 로 나오는 것을 볼 수 있습니다.

$ sudo sh -c 'echo > x1' > x2

$ ls -l x1 x2
-rw-r--r-- 1 root   root     1 2017-01-25 02:44 x1
-rw-rw-r-- 1 mug896 mug896   0 2017-01-25 02:44 x2

참고로 예제로든 sudo 명령은 다음과 같이 할 수도 있습니다.

# sh -c '...' 로 작성하는 명령이 길어지고 quotes 이 포함될 경우 heredoc 을 이용하는 것이 편리하다.
$ sudo sh - <<\@
echo 0 > /proc/sys/kernel/yama/ptrace_scope
@

$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
0
# root 디렉토리에 임시파일을 생성하려고 한 것인데 명령 치환은 
# sudo 명령이 실행되기 전에 처리되므로 permission 오류가 발생한다.
$ sudo ls -l "$(mktemp -p /)"
mktemp: failed to create file via template ‘/tmp.XXXXXXXXXX’: Permission denied
ls: cannot access '': No such file or directory

$ sudo sh -c 'ls -l "$(mktemp -p /)"'                            # OK
-rw------- 1 root root 0 Mar  5 11:14 /tmp.bAWbb8rv87

$ ls -l "$(sudo mktemp -p /)"                                    # OK
-rw------- 1 root root 0 2020-03-05 11:27 /tmp.o61HmpZI4p

Help 문서 보는법

shell builtin 명령의 help 문서를 볼 때는 help builtin 명령을 이용합니다. 외부 명령은 man 페이지, info 페이지를 이용하거나 command --help 형태로 조회할 수 있습니다.

명령 사용법에 나타나는 [...] 로된 부분은 옵션을 의미합니다. 그러니까 명령을 작성할 때 포함할 수도 있고 안할 수도 있다는 의미입니다.

# [ in WORDS ... ] 부분은 옵션입니다.
$ help for  
for: for NAME [in WORDS ... ] ; do COMMANDS; done

Shell builtins, keywords

help command

$ help set       # shell builtin 
set: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
    Set or unset values of shell options and positional parameters.
. . .

$ help test      # shell builtin
test: test [expr]
    Evaluate conditional expression.
. . .

$ help [[        # shell keyword
[[ ... ]]: [[ expression ]]
    Execute conditional command.
. . .

외부 명령

  1. command --help

  2. man command

  3. info command

man page ( manual page ) 는 unix 에서 전통적으로 사용해온 문서 형식입니다. man 명령을 이용하면 외부 명령의 도움말뿐만 아니라 프로그래밍에 사용되는 함수들의 도움말도 모두 볼 수가 있습니다. 뒤에 숫자를 붙여서 섹션 별로 구분을 하는데 그 의미는 아래와 같습니다. 찾고자 하는 이름이 외부 명령에도 존재하고 라이브러리 함수에도 존재할 경우 따로 섹션 번호를 지정하지 않으면 섹션 번호 순서에 따라 외부 명령의 도움말이 출력됩니다.

man 섹션넘버 intro 로 각 섹션의 소개를 볼 수 있습니다.

섹션 설명 예제
1 User Commands man grep
2 System Calls man 2 write
3 C Library Functions man 3 printf
4 Devices and Special Files (usually found in /dev) man 4 tty
5 File formats and conventions e.g /etc/passwd, /etc/crontab man 5 proc
6 Games
7 Miscellaneous (including macro packages and conventions) man 7 signal
man 7 hier
8 System Administration tools and Deamons (usually only for root)  

추가적인 pthread 관련 함수는 glibc-doc, manpages-posix-dev 패키지를 설치하면 볼 수 있습니다.
웹에서도 볼 수 있습니다. http://man7.org/linux/man-pages/

man -f page

해당 페이지가 속한 섹션들을 짧은 설명과 함께 볼 수 있습니다.

$ man -f printf
printf (3)           - formatted output conversion
printf (1)           - format and print data

man -k regex

페이지 이름과 설명에서 단어를 검색할 수 있습니다.

$ man -k printf
asprintf (3)         - print to allocated string
dprintf (3)          - print to a file descriptor
fprintf (3)          - formatted output conversion
fwprintf (3)         - formatted wide-character output conversion
printf (1)           - format and print data
. . .

tab 자동완성

man 명령을 사용할때 tab 자동완성을 사용할 수 있습니다.

$ man 3 str[tab]     # C library functions
strcasecmp   strcspn      strfromd     strncat      strrchr      strtok       strtoumax
strcasestr   strdup       strfromf     strncmp      strsep       strtok_r     strtouq
strcat       strdupa      strfroml     strncpy      strsignal    strtol       strverscmp
. . .

$ man 2 re[tab]      # system calls
read              readlinkat        recvfrom          rename            restart_syscall
readahead         readv             recvmmsg          renameat          
readdir           reboot            recvmsg           renameat2         
readlink          recv              remap_file_pages  request_key

manpath 추가하는 방법

새로 설치한 프로그램에서 man page 파일을 제공하지만 볼수 없을 경우는 다음과 같이 경로를 추가할 수 있습니다. 기본적으로 /path/to/some/bin 경로가 PATH 환경변수에 설정되면 /path/to/some/man or /path/to/some/share/man 같은 경로가 자동으로 추가됩니다.

$ cat ~/.manpath
# 
# "manpath -d" 명령을 이용해 선택되는 경로를 조회할 수 있습니다.
#
MANDATORY_MANPATH /usr/local/texlive/2020/texmf-dist/doc/man

man page 는 문서에 별다른 기능이 없어서 복잡한 명령일 경우 내용이 빈약한것이 단점입니다. 그래서 1990년대 초에 GNU 에서 기존의 man page 를 대체하기 위해 나온것이 info 입니다. info 는 문서에 hyperlink 기능과 간단한 markup language 를 사용할 수 있어서 man page 보다 상세한 내용을 볼 수 있습니다.

$ info grep

# pinfo 패키지를 설치하면 좀 더 쉽게 내용을 볼 수 있습니다.
$ pinfo grep

C++ 언어 도움말

C++ 언어 man 페이지는 따로 제공되지 않습니다. 하지만 cppman 명령을 이용하면 cppreference.comcplusplus.com 사이트에서 제공하는 정보를 볼 수 있고 -c 옵션을 이용해 cache 하면 offline 에서도 볼 수 있습니다.

$ pip3 install cppman

$ cppman vector::push_back
. . .

Quiz

외부 명령의 존재 여부를 체크하려면 어떻게 할까요?

외부 명령의 존재 여부를 체크할 때 type, hash, command 명령을 사용하는 것은 좋은 방법이 아닙니다. 왜냐하면 export 한 function 이나 alias 도 ok 가 됩니다. 따라서 which 명령을 사용하거나 bash 의 경우 type -P 를 사용할 수 있습니다.

# gawk 외부 명령이 존재하는지 체크

command=gawk

if which "$command" > /dev/null; then ...

if type -P "$command" > /dev/null; then ...   # bash only

2 .

어떤 외부 명령을 실행할 때마다 사용된 임시파일이 삭제되지 않고 남아서 계속 쌓이고 있습니다. 명령 실행 후에 매번 직접 삭제하고 있는데 자동화하려면 어떻게 할까요?

명령을 실행할 때 외부명령 보다 shell 함수가 우선순위가 높으므로 동일한 이름의 shell 함수를 작성해서 사용하면 됩니다.

uftrace()
{
    local cmd=`type -P $FUNCNAME`      # $FUNCNAME 값은 uftrace 함수 이름이 된다.
    local exit_status
    if [ -n "$cmd" ]; then             # 명령이 존재하면
        "$cmd" "$@"                    # 실행 후
        exit_status=$?                 # 종료 상태 값을 $exit_status 변수에 저장
    else
        echo "$FUNCNAME: command not found"
        return 127
    fi
    rm -f /dev/shm/uftrace-*           # 임시파일 삭제
    return $exit_status                # return 값으로 저장해둔 $exit_status 값을 반환.
}

--------------  또는 RETURN trap 을 사용 -------------

uftrace()
{
    local cmd=`type -P $FUNCNAME`                   # RETURN trap 을 사용할 경우
    if [ -n "$cmd" ]; then                          # 함수가 종료되기전 마지막으로
        trap 'rm -f /dev/shm/uftrace-*' RETURN      # 실행된 명령의 종료 상태 값이
        "$cmd" "$@"                                 # 자동으로 설정됩니다.
    else
        echo "$FUNCNAME: command not found"
        return 127
    fi
}

3 .

외부 명령이 여러 단계의 symbolic link 로 연결되어 있는 경우가 있는데 전체 링크 목록을 보려면 어떻게 할까요?

namei 외부 명령을 사용하면 됩니다.

$ type -a awk
awk is /usr/bin/awk

$ dpkg -S /usr/bin/awk
dpkg-query: no path found matching pattern /usr/bin/awk

$ namei /usr/bin/awk
f: /usr/bin/awk
 d /
 d usr
 d bin
 l awk -> /etc/alternatives/awk
   d /
   d etc
   d alternatives
   l awk -> /usr/bin/gawk
     d /
     d usr
     d bin
     - gawk

$ dpkg -S /usr/bin/gawk  
gawk: /usr/bin/gawk        # /usr/bin/awk 명령은 gawk 패키지에 포함되어 있는 것을 알 수 있다

명령에 포함된 symbolic link 를 전부 해석해서 physical 절대 경로를 구할 때는 readlink 명령을 사용합니다.

$ type -a google-chrome
google-chrome is /usr/bin/google-chrome

$ readlink -e /usr/bin/google-chrome
/opt/google/chrome/google-chrome       # 실제 실행파일 경로

4 .

  • 스크립트를 작성하다가 ascii 테이블이 보고 싶은데 어떻게 하면 될까요?
$ man ascii
. . .
  • stdio.hstdlib.h 같은 헤더 도움말은 어떻게 볼 수 있을까요?
$ man 7 std[tab]
stdarg.h   stdbool.h  stddef.h   stdint.h   stdio.h    stdlib.h 

$ man stdio.h
. . .