1. 쉘 정의

아래의 내용은 sh 에서만 정확이 작동한다.

쉘이란 무엇인가?

OS와 상호작용하기 위한 유저 인터페이스 프로그램. OS 커널의 가장 외부 레이어 임으로 Shell 이라고 명명되었다.

Unix 란?

Operating System 이란 파일과 프로그램들의 집합이다.

중요 컨셉

  • 쉘은 프로그래밍이 가능한 커맨드 라인 인터프리터이다.
  • 유저는 쉘을 통해 시스템과 상호작용을 할 수 있음
  • 순차적인 조작들은 해당 조작들을 파일에 기록 할 수 있음
  • 쉘은 완전한 프로그래밍 언어임, 변수, 분기문, 다들 프로그램을 실행 시키는것등의 모든 기능이 있음
  • 쉘은 다른 모든 unix 프로그램들과 같은 권한(기능)을 가진 프로그램을 unix 에서 first-class citizen 으로 프로그램을 쉽게 만들수 있게 함.

first class citizen

프로그램 디자인에서 특정 프로그래밍 언어의 first class citizen 이란. 다른 엔터티들에게 일반적으로 가능한 오퍼레이션이 가능한 엔터티를 의미한다.

특징

쉘은 많은 부분을 한개의 명령어가 아닌 쉘에서 직접 처리 할려고 한다. 예를 들어 파일 이름으로 확장되는 문자 * 를 고려해 보자.

Dos

dos 의 경우 모든 파일을 의미 하는 * 의 경우 각각의 명령어가 * 를 파싱해 파일로 확장하는 책임을 진다. 새로운 명령어를 만들면 * 을 처리하는걸 해당 명령어가 해야 한다.

  • RENAME .OLD .NEW
  • COPY, MORE...

  • 을 확장해서 파일 명으로 바꾸는 부분은 쉘의 책임이다. 프로그램들은 확장된 파일 명을 받는다. 만약 DIR 이라는 명령어를 만들고 싶다면 아래와 같이 하면된다.

  • DIR

#!/bin/sh
ls $*

Unix 철학

Unix 철학에서 프로그램의 최종 인터페이스를 쉘언어로 하는 걸 추천한다. 일단 쉘로 특정 기능을 만든다. 그리고 필요하다면 특정 부분을 다른 언어로 대체 한다.

자신이 사용하는 쉘 확인

echo $SHELL

sh 사용하기

Borune 쉘은 각각의 라인을 실행 시킨다. 만약 파일 끝 심볼이 오면 종료 되고 원래 쉘로 리턴한다. (ctrl+d)

$ sh

쉘 기초

쉘은 아래와 같이 동작한다. 0. 라인을 읽은다. - 파일, 스크립트, 유저로 부터. 0. 매타 문자 처리 - 변수 값 치환 - 파일 이름 확장. 0. 실행 가능한 이름을 찾는다?(the name of executable is found) 0. 인자들이 프로그램에 넘어간다. 0. 파일의 rediection을 설정한다. 0. 프로그램이 실행된다.

메타 문자 처리

변수값 치환(Varaible evaluation)

"$"로 시작되는 변수명의 값구하기

파일이름 확장

패턴을 받아 매칭되는 파일 이름들을 리턴한다.

echo  테스트 문자.
  • 파일 확장 특수 문자 처리
    • "?": 1개 문자
    • "*": 0개 또는 여러 문자.
    • "[": 특정 문자 범위
      • "-" 은 범위를 의미
Pattern Matches
* Every file in the current directory
? Files consisting of one character
?? Files consisting of two characters
??* Files consisting of two or more characters
[abcdefg] Files consisting of a single letter from a to g.
[gfedcba] Same as above
[a-g] Same as above
[a-cd-g] Same as above
[a-zA-Z0-9] Files that consist of a single letter or number
[!a-zA-Z0-9] Files that consist of a single character not a letter or number
[a-zA-Z]* Files that start with a letter
?[a-zA-Z]* Files whose second character matches a letter.
*[0-9] Files that end with a number
?[0-9] Two character filename that end with a number
*.[0-9] Files that end with a dot and a number

유닉스 관습

문자 "."은 메타 문자가 아니지만 관습적으로 의미를 같는다.

  • "."으로 시작되는 파일은 일반적으로 리스트로 나열되지 않는다.(ls 등)
  • 모든 디렉토리에 존재하는 히든 파일 2개
    • "." 현재 디렉토리를 의미
    • ".." 부모 디렉토리를 의미
  • "/" 는 디렉토리 패스를 구분하는 특수한 의미를 같는다.
    • 그러므로 "/" 는 파일이름안에 사용할수 없다.
    • 그러므로 파일 확장은 "/" 문자열 이상 확장하지 않는다.
  • 쉘은 파일 확장의 결과를 알파벳 순으로 정렬해서 돌려준다.

  • 원본디텍로리

ls -al
.
..
.hidden
a.sh
b.sh
sub/c.sh
sub/.hidden
  • " * " 을 할 경우 관습상 "." 으로 시작하는 파일을 나열하지 않음
echo *
a.sh
b.sh
  • ".*" 을 할 경우 "."으로 시작하는 히든파일 나열
echo .*
.hidden
  • "a-b*" 를 할 경우 a-b 로 시작되는 모든 파일명을 리턴
a-b-c.sh
a-b-e.sh

echo a-b*
a-b-c.sh  a-b-e.sh
Pattern Matches
* All non-invisible files
abc/* All non-invisible files in directory abc
abc/.* All invisible files in directory abc
/ All non-invisible files in all subdirectories below
/. All invisible files in all subdirectories below

실행 명령어 찾기

쉘이 명령 라인을 확장한 다음에, 라인을 단어들로 분리한다. 그리고 첫번째 단어를 명령어 이름으로 가정한다. 디렉토리가 명시적으로 기술되지 않았다면,이름을 찾을때 까지 PATH 변수의 디렉토리들을 검색한다.

" * " 확장

  • 은 유효한 유닉스 커맨드다.
0.sh
!# /bin/sh
echo Hey!

0.sh 가 있는 디렉토리에서 "*" 을 칠 경우 0.sh 로 확장 되고 첫번째 단어를 명령어로 생각하고 실행시킨다. ? 하지만 에러가 발생한다. Why?

sh-3.2$ *
sh: 0.sh: command not found

인용 부호

쉘이 이해 할 수 있는 특수 문자들을 메타 문자들 이라고 부른다. 기본적으로는 쉘에서 인용 부호안의 문자들은 특수한 의미를 가지지 않는다.

"$" 문자는 메타 문자 이며 쉘에게 다음 단어는 변수임을 알려준다. 만약 일반 캐릭터로 하고 싶다면 인용부호로 감싸면 된다.

옵션

rm -i file1 file2
  1. 쉘은 라인을 4개의 단어로 분리한다.
  2. 첫 번째는 명령어 또는 실행 시킬 프로그램이라고 가정
  3. 뒤 3 단어는 인자로 프로그램으로 넘어간다.
  4. -i 도 파일과 같은 인자일 뿐이다. 쉘은 차이를 알지 못한다.
  5. 프로그램에서 -i 를 옵션으로 다룰 뿐이다. - 는 유닉스에서 옵션을 나타내는 관습이다.

인용부호 토글

유닉스에서 파일이름에 " " 를 사용 할 수 있다. 예를 들어 파일 "file1 file2"이 있다고 하자. rm 'file1 file2' 로 쓸수 있다. 유닉스에서 인용부호는 토글이라고 생각하면 된다. 처음 인용 부호를 사용하면 인용 상태(On Quoated state)를 온하게 되고 이 후 부분의 문자열의 모든 문자를 일반 문자로 인식한다. 두 번째 사용하면 인용 상태를 off (Off Quoated state)하게 되고 이후 문자열에서 메타 문자를 인식한다.

아래는 전부 동일한 의미이다.

rm 'file1 file2'
rm file1' 'file2
rm f'ile1 file'2

메타 문자 escape

3가지 방법이 있다. - 인용문자 사용 - '': 강함 (모든 메타 문자의 특수 의미를 없앰) - "": '' 과 같은 의미지만하지만 약함 (일부 메타 문자는 여전이 특수 의미가 있음) - \ 사용 하면 바로 다음 문자만 escape

내재된 인용 부호

인용마크 안에 존재하는 인용마크의 관계에 대해 생각해보자. 단 내재 인용 마크는 늘 일관적이지 않다.

겹따움표 내재

echo '"'
> "
echo "\""
> "
echo \"
> "

백슬래시 내재

echo '\'
> \
echo "\\"
> \
echo \\
> \

홑따옴표 내재

#error echo '\'' === echo '
echo "'"
> '
echo \'
> '

정리

순번 Escape 문자 내재 " 내재 \ 내재 '
1 ' '"' '\' error '\'' === echo '
2 " "\"" "\\" "\'"
3 \ \" \\ \'
  • 겹따옴표와 홑따옴표는 다르게 동작한다.
  • 두 번째 줄 겹따옴표로 감싼 상태에서 escape 문자 \를 여전이 메타 문자로 인식 함으로 겹따움표를 약한 인용부호라고 불린다.

강한 또는 약한 감싸기

  • 홑따옴표는 강해서, 감싸진 모든 문자열을 보통 문자로 처리한다.
  • 겹따움표는 약해서, 감싸진 부분에서도 몇몇 문자를 메타 문자로 인식한다.
    • \ escpae 문자.
    • $ 달러 문자.
    • ` 백쿼트 문자.

$ 문자.

  • $로 시작하면 변수이다.
  • 홑따옴표에서는 문자열이 나온다.
  • 겹따옴표에서는 변수의 값이 나온다.
echo '$HOME'
>$HOME
echo '\$HOME'
>\$HOME
echo "$HOME"
>/Users/younlee
echo "\$HOME"
>$HOME

` 백쿼트 문자.

  • 명령어(command) 를 실행 시켜 결과 값으로 치환한다.
echo 'The current dircetory is `pwd`'
> The current dircetory is `pwd`
echo 'The current dircetory is \`pwd\`'
> The current dircetory is \`pwd\`
echo " current dircetory is `pwd`"
> current dircetory is /Users/younlee/Dropbox/home
echo " current dircetory is \`pwd\`"
> current dircetory is `pwd`

여러줄에서 사용 가능한 인용문자.

C 쉘에서 인용문자가 끝나지 않으면 라인과 끝난다고 가정한다. 본쉘에서는 인용문자를 닫을때까지 인용상태로 들어간다.

echo 'a
quote> b
quote> c
quote> '

? !는 무슨 의미를 같는가?

인용문자안의 인용문자.

  • 보통 언어에서 스트링을 시작하는 메타 문자가 있고(일반적으로 인용문자) 해당 문자 안에서 특수 문자를 사용 하려면 escape 문자를 사용한다.
  • 하지만 본쉘에서 인용문자는 스트링을 정의 하는대 사용되지 않는다. 메타 문자를 해석하는 기능을 키느냐 끄느냐로 상용된다.
echo 'a'"b"'c'"d"
>abcd
  • 서로 다른 인용 문자를 연결해서 내부 인용 문자를 일반 문자로 출력 할 수 있다.
echo '"a'"'b"
> "a'b

스트링 안에 변수 넣기

  • 여러 라인의 sed 또는 awk 스크립트를 사용할때 아래처럼 하면 편하다.
echo "My home dircetory is $HOME, and my account is $USER"
> My home dircetory is /Users/younlee, and my account is younlee
echo 'My home dircetory is '$HOME', and my account is '$USER
> My home dircetory is /Users/younlee, and my account is younlee

변수

  • 간단한 문법사용
variable=value
  • 변수이름은 문자, 숫자, _ 가 허용됨 (숫자로 시작하면 안됨)
  • 스페이스 즉 화이트스페이스(스페이스, 탭, 뉴라인) 캐릭터는 값의 배정을 끝냄.
  • 만약 화이트스페이캐릭터를 사용하고 싶다면 인용 부호로 감싸야함
varirable='What is ?'
  • 여러 변수를 한줄에 선언 가능
A=1 B=2 C=3
  • 등호뒤에 스페이를 쓰면 빈문자가 들어감
a= date # a 의 값은 빈문자열.
a =date # command a를 실행할려고 함

경고

명령어들을 한 라인에 적고 싶을때는; 을 꼭 사용하자. 스페이스로 구분하면 각각의 쉘마다 다르게 작동한다.

a=one; echo $a # 좋음
a=two echo $a # 라인으로 읽은다음 메타 문자 확작을 먼저 하기 때문에 one 이 출력됨 a 에 값도 할당안됨 왜지?
a=three echo $a >$a # 쉘마다 다른결과

set 명령어

bash 에서 정의한 변수 명을 보고 싶다면 set 을 쓰자. 알파벳 순서로 정렬된 변수들을 리스트 한다.

$ set 
> ANDROID_HOME=/Users/younlee/Library/Android/sdk
> ANDROID_NDK_HOME=/Users/younlee/Library/Android/sdk/ndk-bundle

환경 변수

유닉스는 환경 변수를 사용해 자식 프로세스에게 정보를 전달 할 수 있다. 로그인 하면 미리 정의된 환경 변수가 주어 진다. start up script 에 필요한 변수를 더 추가 할 수 있다. 유저가 프로그램을 실행 한다는것은 자신이 로그인하면서 받은 프로세스의 자식으로 새로운 프로세스를 만드는것이기 때문에 환경 변수를 상속한다. 환경 변수는 부모에서 자식으로 한방향으로 흐른다. 자식이 부모의 값을 변경 할 경우 값의 정합성을 지킬수 없기 때문이다. ?자식 프로세는 자신의 현재 디렉토리를 변경 할 수 잆다. 왜냐하면 부모에게 상속 받았기 때문이다.

export 명령어

인자 없는 export는 export 된 environment 변수들을 보여준다. export 하면 자식 프로세스에게 환경 변수가 상속된다. export 를 사용하면 env 변들를 업데이트 한다.

  • 쉘 변수 a, b, c 를 자식들이 현재 갑을 상속하게 한다.
export a b c 
  • 환경 변수 값을 바꾸고 export 안 할 경우 자식은 원래의 값을 가진다.
# 부모 프로세스- 익스포트 안함
$test=1
$echo $test
>1

# 자식 프로세스- 결과 없음
$sh 
$echo $test
>  
$ exit

# 부모 프로세스 - 익스포트
$export test

# 자식 프로세스 - 상속함
$sh 
$echo $test
  • export 로 mark 하면 값을 카피 하지않고 참조함.
# 부모 프로세스 - 익스포트 후 값 변경
$test=1
$export test
$test=2

# 자식 프로세스 
$sh 
$echo $test
>2

특별한 환경 변수들.

각 OS 마다 다른 종류의 특별한 환경 변수들을 가지고 있음

PATH - 탐색 경로

명령어를 포함하고 있는 디렉토리들을 나열함. 만약 커맨드를 실행 시키면 나열된 순서대로 찾아서 가장 빠른걸 실행 콜론은 디렉토리 구분자로 사용, 빈 스트링은 현재 디렉토리를 의미 현재 디렉토리에 ls 등의 명령어가 있으면 먼저 찾아 짐으로 마지막에 붙이는걸 추천

  • 3개의 탐색 경로를 패스에 할당
/usr/bin:/usr/ucb:

HOME - 홈 디렉토리

cd 할때 인자가 없는 곳을 정의 로그인 포르세스에서 SET 함

CDPATH - cd search path

cd 명령어를 사용할때 탐색하는 디렉토리 패스 현재 디렉토리를 검색하고 나열된 디렉토리를 순차적으로 검색

IFS - Internal Field Seperator

단어 종결을 의미하는 문자 리스트.

  • 스페이스, 탭, 줄바꿈, ?
IFS=$' \t\n\C-@'

PS1 - Nomal Propmt

쉘라인 앞에 나오는 문자$ - zsh 에서 안됨

sh-3.2$ PS1="@@"
@@

PS2 - Secondary Propmt

멀티라인 커맨드 할 때 나옴

  • >< 로 변경
sh-3.2$ a='
> '
sh-3.2$ PS2="<"
sh-3.2$ a='
<'

변수 변환 예제

  • 변수 X 가 있다. X 값에 .old 를 더해 보자.
touch account
x=account
mv $x $x.old
  • 변수 X 가 있다. X 값에 _old 를 더해 보자.
    • 에러가 발생한다.
    • 왜냐하면 _ 는 적합한 변수 이름이기 때문이다.
    • 쉘은 account_old 라는 변수명의 값이 빈 문자열 이기 때문에 빈 문자열을 리턴
touch account
x=account
mv $x $x_old

인용문자를 통한 풀이

  • 변수 X 가 있다. X 값에 _old 를 더해 보자.
touch account
x=account
echo $x"_old"
echo $x'_old'
echo $x\_old
echo $x''_old
echo $x""_old
echo "$x"_old

중괄호를 사용하여 풀기

  • Bracket, 괄호
    • [] Square bracket or simple bracket, 대괄호
    • {} Curly bracket or braches, 중괄호
    • () Round bracket or paranthes, 소괄호
    • 영문표
  • Unix의 make utilty 에서 관습적으로 1자리 이상의 변수를 사용할때 중괄호를 사용하게 되어 있다.
${x}run

중괄호 특수 기능

변수뒤에 기호와 값을 주어 정의가 안되어 있을 경우의 행동을 지정 가능.

Form Meaning
${vaiable?words} 정의 안되어 있으면 words 를 에러 메시지로 표현
${vaiable-words} 정의 안되어 있으면 words 를 값으로 리턴(변수는 여전이 정의 되지 않음)
${vaiable+words} 정의 되어 있으면 words를 값으로 리턴 (변수는 여전이 정의된 값)
${vaiable=words} 정의 안되어 있으면 words 를 변수의 값으로 정의하고 값을 리턴
  • 정의 되어 있으면 words를 값으로 리턴 (변수는 여전이 정의된 값)
var2=1
echo $var2
>
echo ${var2+0}
> 0
echo $var2
> 1
  • 정의 안되어 있으면 words 를 변수의 값으로 정의하고 값을 리턴
echo $var3
>
echo ${var3=0}
> 0
echo $var3
> 0

${var?words} 정의 안되어 있으면 에러 리턴

정의 안되어 있으면 words 를 에러 메시지로 표현 - 에러 메시지가 잘 나오기 때문에 디폴트로?를 붙이는걸 추천.

echo $var0
>
echo ${var0?'error msg'}
> var0: 'error msg'

$ {varaible-default} 정의 안되어 있으면 디폴트 값을 리턴

정의 안되어 있으면 words 를 값으로 리턴(변수는 여전이 정의 되지 않음)

echo $var1
>
echo ${var1-0}
> 0
echo $var1
>
${b-string}
${b-$var1}
${b-"a b c"}
${b-"a b c  $HOME or `date`"}
${b-`wc -l </etc/passwd`}

변수를 undefined 로 만들기

A=  # 빈스트링으로 변수 설정.
unset A B # undefined 가 됨.

Null("empty string") vs undefined

널 변수와 undefiend 는 다르다. B를 선언하지 않고 A=$B 를 하면 변수를 선언 했지만 Null 이 된다. Null 과 undefined 둘다 적용해주고 싶다면

Form Meaning
${vaiable:?words} 변수가 null or undefiend 이면 words 를 에러 메시지로 표현
${vaiable:-words} 변수가 null or undefiend 이면 words 를 값으로 리턴(변수는 여전이 정의 되지 않음)
${vaiable:+words} 위와 반대
${vaiable:=words} 변수가 null or undefiend 이면 words 를 변수의 값으로 정의하고 값을 리턴
b="" # equal b=$a
c="Z"
echo a=${a-1}, b=${b-2}, c=${c-3}
>a=1, b=, c=Z
echo a=${a:-1}, b=${b:-2}, c=${c:-3}
>a=1, b=2, c=Z
  • 원본 아래 내용은 개인용으로 정리한 내용 입니다.