Shell

Shell에서의 Exit Status에 관하여

Shell에서의 Exit Status에 관하여

Exit Status에 대하여 알아보자.

개요

지금은 소프트웨어프로그램이라고 하면 사람들은 거의 그래픽 UI를 가지는 프로그램을 생각 할 것이다. 하지만 Linux와 같은 UNIX계열의 운영체제에서는 쉘(SHELL)에서 많은 일을 수행한다

리눅스에서 어떤 프로그램을 설치 하기위해 구글링 하면 거의 대부분은 쉘에서 패키기 관리자(apt, dnf, pacman 등)를 실행하여 프로그램을 설치 할 것이다.

복잡한 여러 명령을 반복적으로 수행해야 할때 쉘 스크립트를 작성하면 이러한 반복적인 작업을 매우 쉽고 빠르게 할 수 있다.

우리는 쉘에서 명령어를 실행 할때 해당 명령의 출력을 통해 프로그램이 정상적으로 실행되었는지를 판단 할 수 있다. 실행 도중 에러가 발생 하였다면 화면에 에러가 출력 될것이다.

전통적으로 UNIX 계열 프로그램은 ls와 같이 명시적으로 정보를 출력을 요구 하는 명령이 아닌 경우 정상적으로 동작 하였을 경우 아무런 출력이 없는 경우가 많다. 이는 침묵은 금이라는 유닉스의 설계 철학에 따르는 것이다.

Speech is silver, but silence is gold.

그렇다면 쉘에서 실행한 명령이 정상적으로 수행되었는지 어떻게 판단 할까?

우리가 일반적으로 사용하는 방법은 명령이 실행되면서 출력되는 출력 값을 경험적으로 알고 있기 때문에 이 출력값을 기반으로 명령이 성공적으로 수행되었는지 확인한다. 하지만 이 방법은 사람에게는 매우 효율적인 방법이지만 쉘스크립트를 짜는 프로그래머 입장에서는 매우 번거로운 방법이다.

더 일반적으로 사용할 수 있는 방법을 찾아 보자.

모든 명령은 종료될 때 waitpid 시스템 콜(이나 이와 동등항 함수)에서 Exit Status를 반환하는데 (명시적으로 지정하지 않을 경우 기본값(0)을 반환한다.) 프로그래머는 이 Exit Status를 가지고 자신의 실행한 명령이 정상적으로 실행 되었는지 판단 할 수 있다.

Exit Status

Exit Status는 O 에서 255 사이의 값을 가지고 쉘의 경우 125 이상의 값을 사용한다.

어떤 SHELL을 사용하고 있는지 확인해보자.

어떤 SHELL을 사용하고 있는지 확인해보자.

어러 쉘이 있다. sh(Bourne Shell)을 기반으로 하는 Bash(Bourne-again shell), zsh(Z shell) 등은 문법이 거의 똑같지만 완전히 같지는 않다. 쉘스크립트를 만들어 실행 하는 경우 #! 지시자로 사용할 쉘을 지정할 수 있기 때문에 크게 문제가 없지만 source 명령이나 dot(.) 명령으로 현재 환경(Current Environment Context)에서 스크립트를 실행하는 경우 사용중인 쉘의 종류에 따라 문법이 달라 문제가 발생 할 수 있다. 주의해서 처리 하도록 하자.

기본쉘을 Zsh 사용하기 시작하면서 겪은 문제다. 보통 리눅스에서 기본쉘은 Bash 이기 때문에 거의 모든 프로그램들이 Bash 위주로 구현되어 있기 때문에 발생 하는 문제이다.

대부분 다음과 같은 문제이다.

실행 스크립트의 파일 이름을 알고 싶을때 Bash에서는 $BASH_SOURCE를 사용한다. 하지만 이 변수는 Bash 에서만 지원됨으로 Zsh에서는 $0 변수를 사용해야 된다. Bash에서도 $0변수를 지원하지만 Bash에서 $0 변수를 사용하면 쉘의 정보((bash)가 표시된다.

쉘스크립트에서 $0 는 실행파일의 이름을 나타내는 변수 이다. Bash에서 $0 변수는 쉘이 시작할 때만 초기화 되지만 dot 명령은 새로운 쉘을 시작하는 것이 아니라 현재 쉘의 컨텍스트에서 실행 하는 것이므로 쉘에서 echo $0 를 실행한것과 같은 결과(즉, bash)가 출력된다. 그런데 Zsh 에서는 $0가 스크립트 이름으로 세팅된다.

구글링을 해보면 Zsh에서 Bash$BASH_SOURCE 와 가장 가까운 결과를 내는것은 ${(%):-%x} 라고 한다. 하지만 $0도 같은 결과는 내기 때문에 그냥 사용하기로 한다.

다음은 ./env 파일의 내용이다.

1
2
echo \$BASH_SOURCE = $BASH_SOURCE
echo \$0 = $0

각 SHELL에서 실행 해 보자

. ./env

다음과 같은 출력을 얻을 수 있다.

bash

$BASH_SOURCE = ./env
$0 = bash

zsh

$BASH_SOURCE =
$0 = ./env

따라서 실행 되는 스크립트의 이름 알기 위해서는 다음과 같이 쉘에 따라 다른 방식을 정용하여야 한다.

SH_NAME=$(basename $(readlink /proc/$$/exe))

case ${SH_NAME} in
	"zsh") SELF=$(realpath $0) ;;
	"bash") SELF=$(realpath ${BASH_SOURCE[0]}) ;;
	*) echo "Unknown SHELL $SH_NAME" && exit 0 ;;
esac

echo $SELF

Bash 또는 Zsh 만 사용한다면 다음과 같이 사용해도 같은 결과를 얻을 수 있다.

SELF=$(realpath ${BASH_SOURCE[0]:-$0})
echo $SELF

아래와 같이 env 파일을 만들고 dot command로 실행해보자

SELF=${BASH_SOURCE[0]:-$_}
echo $(basename $SELF) is located at $(realpath $SELF)

env 파일 실행

. ./env

Bash, Zsh 둘다 같은 결과를 얻을 수 있다.

env is located at /home/euikook/src/env

$SHELL

/etc/password 파일에 정의된 사용자에 할당된 SHELL 출력한다.

echo $SHELL

Cons

지정된 쉘 정보를 표시 하지만 현재 사용중인 SHELL 정보를 출력하지 않는다.

$ cat /etc/passwd | grep $USER
john:x:1000:1000::/home/john:/usr/bin/zsh

cat 명령으로 /etc/passwd 파일을 확인 결과 /usr/bin/zsh 가 쉘로 할당되어 있다.

아래와 같이 bash로 쉘을 변경 후 $SHELL$0 정보를 출력 해보자.

$ bash
$ echo echo User SHELL is $SHELL, Current SHELL is $0
User SHELL is /usr/bin/zsh, Current SHELL is bash
Let use zsh

Let use zsh

Let use zsh

.zshrc

ZSH=/usr/share/oh-my-zsh/
ZSH_THEME="agnoster"
DISABLE_AUTO_UPDATE="true"
plugins=(git virtualenv)


ZSH_CACHE_DIR=$HOME/.cache/oh-my-zsh
if [[ ! -d $ZSH_CACHE_DIR ]]; then
  mkdir $ZSH_CACHE_DIR
fi

source $ZSH/oh-my-zsh.sh
source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source /usr/share/nvm/init-nvm.sh

prompt_context() {
  if [[ "$USER" != "$DEFAULT_USER" || -n "$SSH_CLIENT" ]]; then
    CONTEXT="%(!.%{%F{yellow}%}.)%n"
    if [[ -n "$SSH_CLIENT" ]]; then
      CONTEXT="${CONTEXT}@%m"
      #prompt_segment black default "%(!.%{%F{yellow}%}.)%n@%m"
    fi
    prompt_segment black default ${CONTEXT}
  fi
}


[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*

# Add RVM to PATH for scripting. Make sure this is the last PATH variable change.
export PATH="$PATH:$HOME/.rvm/bin"
쉘에서의 큰따옴표와 작은따옴표(인용부호, Quoting) 그리고 백슬래쉬(Baskslash)

쉘에서의 큰따옴표와 작은따옴표(인용부호, Quoting) 그리고 백슬래쉬(Baskslash)

쉘에서 사용되는 세가지 인용법에 대하여 알아보자.

SHELL에서 인용(Quoting) 이란?

쉘에서 인용(Quoting) 은 특정 문자나 단어가 가지는 특별한 의미(또는 기능)를 제거 하는데 사용된다. 예를 들어 빈칸(white space) 은 쉘에서 인자를 구분하는데 쓰이지만 Quoting빈칸(<whitespace>) 매개변수를 구분하는 기능이 무시된다.

쉘에서 Quoting 메커니즘은 세가지가 있다.

  • 이스케이프 문자(Escape Character): Hello\ World
  • 작은따옴표(Single Quotes, '): 'Hello World'
  • 큰따옴표(Double Quotes, "): "Hello World"

Hello\ World, 'Hello World' "Hello World" 이 세가지 모두 하나의 매개변수로 취급 한다.

쉘에서 아래 명령을 실행해 보자. 다음 명령은 매개변수의 갯수를 나타내는 변수 $# 를 출력한다.

sh -c 'echo $#' Hello World
sh -c 'echo $#' Hello\ World
sh -c 'echo $#' 'Hello World'
sh -c 'echo $#' "Hello World"

결과를 보자.

$ sh -c 'echo $#' echo Hello World
1
$ sh -c 'echo $#' echo Hello\ World
0
$ sh -c 'echo $#' echo 'Hello World'
0
$ sh -c 'echo $#' echo "Hello World"
0
History 파일에서 특정 엔트리 삭제하기

History 파일에서 특정 엔트리 삭제하기

Remove specific history entries from history file

History 파일에서 특정 엔트리 삭제하기

History 파일에서 특정 엔트리 삭제하기

  작업을 하다 보면 민감한 정보가 History에 남아 있는 경우가 있다. 다음과 같이 명령 앞에 white space를 추가 하여 해당 명령을 history 파일에 남기지 않는 방법이 있다.

mysql -u root -ppassword -h localhost 
개발자와 시스템 관리자의 친구 tmux, GNU screen Alternative  - 01

개발자와 시스템 관리자의 친구 tmux, GNU screen Alternative - 01

Usages of tmux, GNU screen Alternative

개발자와 시스템 관리자의 친구 tmux, GNU screen Alternative - 01

개발자와 시스템 관리자의 친구, tmux 에 대하여 알아보자

tmux는 GNU screen을 대체 할 수 있는 가장 매력적인 대안이다. Terminal Multiplex 로 매뉴얼은 다음에서 볼 수 있다.

tmux man page

tmux의 주요 기능에 대하여 알아본다. 먼저 세션 유지 기능과 화면 공유 기능에 대하여 알아보자.

왜 디렉터리 Hard link를 만들 수 없나요?

왜 디렉터리 Hard link를 만들 수 없나요?

왜 디렉터리 Hard link를 만들 수 없나요?

디렉터리 Hard Link를 만들고 싶지만 만들어 지지 않는다.

예전에는 root 권한으로 -d 옵셥을 주면 디렉터리에 대한 Hard Link를 만들 수 있었던것 같은데 지금  Ubuntu 에서 테스트 해보니 root 권한으로도 디렉터리 Hard Link가 만들어 지지 않는다.

mkdir a
ln a b
ln: a: hard link not allowed for directory

왜 디렉터리에 대한 Hard Link를 만들수 없는지 알아보자.