리눅스 쉘 스크립트 맛보기


2.1 리눅스의 부팅 과정과 로그인 쉘

  1. init
    • init과 연결된 수많은 프로세스들이 자신만의 프로세스 ID(PID)를 가지고 생성되기 시작
    • init의 PID는 1
    • init 프로세스가 시스템을 초기화하고 터미널 라인을 오픈하기 위한 작업을 시작하고, 표준 입력과 표준 출력, 표준 에러를 설정
    • ps -ef 를 입력하면 PID와 같이 출력
      • 표준 입력 (0): 키보드로부터 입력받는 것
      • 표준 출력 (1) / 표준 에러(2) : 모니터로 출력하는 것
        • 파일 디스크립터 0 1 2 ( 식별자 )
        • 사용 사례 : bash 스크립트를 작성할 경우 표준 에러를 표준 출력으로 돌리고, 에러인 경우 dev/null로 찍는 경우 등등
        • standard error redirection
        • https://unix.stackexchange.com/questions/111611/what-does-the-rc-stand-for-in-etc-rc-d
    • 그 이후 로그인 프롬포트를 보여줌
    • init 프로세스는 /etc/rc.d/init.d 에 런레벨별로 설정되어 있는 쉘 스크립트를 실행. 이 스크립트들은 chkconfig 명령을 사용해 부팅 시 자동으로 실행할 것인지, 실행하지 않을 것인지 설정할 수 있음
    • 부팅 시 수행할 런레벨은 /etc/inittab에 설정되어 있음( 우분투는 /etc/init/rc-sysinit.conf!)
    • 런레벨은 0부터 6까지 존재하는데, 주로 텍스트 모드 런레벨인 3번(주로 서버)과 그래픽 모드 런레벨인 5번을 주로 사용(주로 데스크탑) 현재는 level 2를 사용 중
    • rc1.d, rc2.d, rc3.d, rc4.d, rc5.d, rc6.d에 설정 파일이 들어가있음 (runlevel configuration)
    • K로 시작하는 것은 kill, S로 시작하는 것은 Start를 의미하며 파일의 prefix를 수정하면 바로 반영
  2. 로그인 프롬포트
    • /bin/login 프로그램이 /etc/passwd 파일에 있는 첫 번째 필드를 체크하기 위해 유저 아이디를 검증하고, 존재하면 패스워드 검증
    • 패스워드가 맞다면 /etc/passwd 파일의 마지막에 설정되어있는 SHELL 변수에는 로그인 쉘을, USER와 USERNAME 변수에는 로그인 이름을 할당
    • 로그인이 끝나면 /etc/passwd 파일에서 유저 라인의 마지막 단계에 입력되어 있는 프로그램을 실행. 일반적으로 배시 쉘로 설정되어 있음

쉘 초기화 파일들

  1. /etc/profile
    • 시스템 전역 쉘 변수 초기화
    • 유저가 쉘에 로그인하면 가장 먼저 이 곳의 파일을 읽음. 이 파일에는 PATH, USER, LOGNAME, MAIL, HOSTNAME, HISTSIZE, INPUTRC 등의 쉘 변수들이 선언되어 있음
    • 전역 리드라인 초기화 파일인 /etc/inputrc 파일을 읽어들이도록 되어 있으며, 프로그램들의 전역 환경을 설정하는 파일을 포함하고 있는 /etc/profile.d 디렉터리를 읽어들이도록 구성되어 있음
  2. /etc/bashrc
    • 쉘 함수와 Alias를 위한 시스템 전역 변수 정의
  3. ~/.bash_profile
    • 유저 개인의 환경 설정 파일
    • 시스템 전역이 아닌 유저 자신만의 PATH와 시작 프로그램을 추가적으로 설정
  4. ~/.bashrc
    • 유저 개인의 Alias 및 변수 설정 파일
    • 유저 자신의 개인적 명령어 앨리아스를 정의할 수 있으며, /etc/bashrc 파일에서 시스템 전역 변수를 읽은 다음, 특별한 프로그램을 위한 변수를 설정할 수 있음
  5. ~/.bash_logout
    • 로그아웃 설정 파일
    • 로그아웃 절차를 포함하고 있음. ex) 로그아웃하면 터미널 윈도우가 사라짐
  6. source 명령
    • 쉘 환경 설정 파일 즉시 적용하기. . 명령도 같은 기능을 수행
  7. zsh의 경우 ~/.zshrc 에 설정 파일이 존재!

명령 라인 파싱

  1. 히스토리 치환
  2. 명령라인은 토큰 또는 단어 단위
  3. 히스토리 업데이트
  4. 인용 진행
  5. 앨리아스 치환 / 함수 정의
  6. 리다이렉션, 백그라운드, 파이프 설정
  7. 변수 치환이 수행
  8. 명령 치환이 수행
  9. globbing 이라는 파일명 치환이 수행
  10. 명령이 실행

명령어 타입

  1. 앨리아스
  2. 키워드
  3. 함수
  4. 빌트인 내장명령
  5. 실행 파일

프로세스와 쉘

  • 프로세스 : 유일한 PID 번호에 의해 식별될 수 있는 실행 프로그램
    • 실행 프로그램의 데이터와 스택, 프로그램 포인터와 스택 포인터 그리고 프로그램을 실행하기 위해 필요한 모든 정보들로 구성
    • 쉘 : 로그인 프로세스를 완료했을 때, 시작하는 특별한 프로그램으로 프로세스임
  • 커널 : 프로세스를 제어하고 관리

시스템 콜

  • 쉘 스크립트로부터 명령을 실행했을 때, 빌트인 내부 코드 / 디스크 저장장치에서 명령을 찾고 실행된 명령을 정렬
  • 커널 영역의 기능을 사용자 모드가 사용 가능하게, 즉 프로세스가 하드웨어에 직접 접근해서 필요한 기능을 사용할 수 있게 해줌

  • fork 시스템 콜 : 콜프로세스의 복사본을 생성
    • 자식 프로세스는 fork가 호출된 다음 실행하고 두 프로세스는 CPU 공유. 자식 프로세스는 부모 프로세스의 환경, 오픈된 파일, 실제적 유저 ID, umask, 현재 작업 디렉터리, 시그널의 복사본을 가짐
    • 명령을 실행했을 때 아래와 같은 Flow를 가짐
      1. 쉘은 명령 라인을 파싱하고 첫 단어가 빌트인 명령인지, 디스크에 존재하는 실행 명령인지 판단
      2. 빌트인 명령 -> 곧바로 처리
      3. 디스크에 존재 -> 부모 쉘의 복사본을 만들기 위해 fork 시스템 콜 호출
      4. 자식 프로세스는 명령을 찾기 위해 PATH에 정의되어 있는 경로 검색하고 리다이렉션, 파이프, 명령 치환, 백그라운드 프로세싱을 위한 파일 디스크립터를 설정
      5. 자식 프로세스가 동작하는 동안 부모 프로세스는 잠시 멈춤
  • wait 시스템 콜 : 자식 프로 세스 하나가 종료될 때까지 부모 프로세스를 대기상태로 유지
    • wait가 성공하면 자식 프로세스가 종료되고, 종료상태를 가지고 있는 자식 프로세스의 PID를 리턴
    • 자식 프로세스가 종료되기 전 부모 프로세스가 종료된다면, init 프로세스는 자식 프로세스를 고아가 된 좀비 프로세스로 만듬
    • 부모를 대기상태로 만드는 것 + 프로세스가 정상적으로 종료하도록 보증하는 역할
  • exec 시스템 콜 : 터미널에 명령을 수행한 다음, 쉘은 새로운 쉘 프로세스를 fork. 자식 쉘은 타이핑된 명령을 수행
    • 쉘은 프로그램을 위해 PATH를 검색 -> 프로그램이 발견되면 명령의 이름과 함께 exec 시스템 콜을 호출
    • 커널은 이 프로그램을 메모리에 로드하며 자식 쉘은 새 프로그램으로 오버랩
    • 새 프로그램은 자식 프로세스가 되고 실행을 시작(모든 환경 변수, 오픈 파일, 유저 정보를 물려받음)
  • exit 시스템 콜 : 중지
    • 자식 프로세스를 종료할 때 자식 프로세스는 sigchild 시그널을 보내고 자식의 종료상태를 부모 프로세스가 받아들이도록 대기. 종료상태는 0~255의 숫자이며 0은 성공적으로 실행되었단 뜻이며 0이 아닌 종료상태는 프로그램 실행리 실패했다는 뜻
  • 프로세스 종료하기
    • CTRL-C 또는 CTRL-\ 키를 이용해 종료할 수 있으며, kill 명령을 사용해 종료할 수 있음.

변수

변수 타입

  • 쉘 변수들은 대문자로 정의하며 2가지의 변수 타입을 가지고 있음
  • 전역 변수
    • 전역 변수 또는 환경 변수들은 모든 쉘에서 사용할 수 있으며, env 명령을 사용하면 환경 변수들을 출력해볼 수 있음
    • echo $PATH : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 이런식으로 나오는데 쉘은 명령어를 찾을 때마다 이 디렉토리를 순서대로 검사함
    • 추가하고 싶으면 vi ~/.bash_profile 에서 export PATH=$PATH:/path/to/add 이런식으로 추가
  • 지역 변수
    • 지역 변수는 현재의 쉘에서만 사용할 수 있음. 옵션 없이 set 빌트인 명령을 사용하면 환경 변수를 포함해 모든 변수들과 함수들의 목록을 보여줌
  • 다음 변수는 배시 쉘 프로그래밍 부분에서 나올 예정 (문자열 변수, 정수형 변수, 상수형 변수, 배열 변수)

변수 생성

  • 변수들은 기본적으로 대문자로 생성. 숫자를 포함할 순 있지만 숫자로 시작할 수 없다
  • = 앞뒤로 공백이 없어야 한다. VAR="value" 이런식!
  • 변수를 출력할 경우엔 echo 를 사용. echo $VAR, echo "$VAR", echo "${VAR}" 모두 동일한 기능 수행
  • ★★★ 큰따옴표(““)를 쓰면 $str의 실제 값을 출력할 수 있고(변수의 값) 작은따옴표(‘‘)를 쓰면 그냥 $str이란 문자열 자체를 출력함!
  • 밖에 큰따옴표를 쓰고 작은따옴표는 안에 쓰는 경우가 많음

지역 변수를 환경 변수로 만들기

  • 현재 쉘에 만든 변수들은 현재 쉘에서만 사용할 수 있음. 이 경우 export 빌트인 명령을 사용하면 지역 변수를 환경 변수로 만들 수 있음
  • 해제는 unset
  • 자식 쉘에서 export 명령을 사용한다고 해서 부모 쉘에 영향을 주지는 못함

특수 파라미터 변수들 (Vㅔ리 중요)

  • $* : 1부터 시작하는 위치 파라미터의 확장, 큰 따옴표로 구분하고 IFS 특수 변수의 첫 문자로 구분되는 각 파라미터의 값으로 하나의 단어를 확장하며, 전체 파라미터값을 가지고 있다. 만약 IFS가 NULL이거나 해제되어 잇으면 파라미터는 스페이스로 구분
  • $@ : 1부터 시작하는 위치 파라미터의 확장이며, 큰 따옴표로 확장되면 각 파라미터는 하나의 구분 단어로 확장되고, 전체 파라미터값을 가짐
  • $# : 십진수의 위치 파라미터 전체 개수를 의미
  • $? : 최근 실행된 포그라운드 파이프라인의 종료상태를 가지고 있음(0은 정상적으로 종료)
  • $- : 실행하자마자 set 내장명령을 통해 또는 쉘 자체에 의해 설정된 현재 옵션 플래그로 확장
  • $$ : 현재 쉘의 프로세스 ID
  • $! : 가장 최근에 백그라운드로 실행된 프로세스의 ID를 가지고 있음
  • $0 :쉘 또는 쉘 스크립트의 이름을 가지고 있음
  • $_ : 아규먼트 목록을 사용해 실행된 쉘스크립트의 절대 경로를 가지고 있음

본 쉘

  • 본 쉘 shbang 라인 : 쉘 스크립트의 첫 라인에 #!/bin/sh를 입력해서 커널에게 이 파일은 본 쉘 스크립트라고 인지해야 함
  • 주석은 #을 사용
  • 와일드 카드 : *,?,[] 는 파일명 확장을 위해 사용되고, <,>,2>,>>,| 문자들은 표준 IO를 위해 사용된다. 문자들이 인터프리터에 의해 해석되지 않도록 하기 위해선 인용부호(', '')로 감싸주면 됨
  • read : 사용자의 입력을 읽고 read 명령의 오른쪽에 적는 변수에 사용자 입력값을 할당

      read  word
      echo "입력한 단어는: $word"
    
  • 조건문
    • if~then 문장을 사용하며 if문의 끝에는 반드시 fi를 적어 if문이 끝났음을 표시
      if [표현식] 
      then 
          명령 문장 블록
      elif [표현식]
      then
          명령 문장 블록
      else
          명령 문장 블록
      fi	
    
    • case문
        case "$color" in
        blue)
            echo $color is blue
            ;;
        green)
            echo $color is green
            ;;
        red|orange)
            echo $color is red or orange
            ;;
        *) echo "Not a color" # default
        esac
      
  • 루프문 : while, until, for 3가지 종류가 존재
    • while : 뒤에 오는 명령이나 표현식이 treu면 계속해서 do와 done 사이 문장 실행
    • until : while와 비슷하지만 until 뒤에 오는 명령이 false가 될 때까지 do와 done 사이 문장 실행. 거의 사용하지 않음
    • for : 단어목록을 통해서 반복, for 다음으로 변수 이름이 오고 in 다음 단어 목록이 온다. 그리고 do와 done 키워드 사이의 문장을 수행
  • 파일 테스팅 : test은 상태 표현식을 판단하기 위해 사용. 파일, 디렉터리, plain 텍스트, 읽을 수 있는 파일 등의 속성을 테스트하기 위해 옵션을 가지고 있음
    • -d : 디렉터리인지 테스트
    • -f : 파일인지 테스트
    • -r : 현재 사용자가 읽을 수 있는지 테스트
    • -s : 파일 크기가 0보다 큰지 테스트
    • -w : 현재 사용자가 파일에 쓰기 가능한지 테스트
    • -x : 현재 사용자가 파일을 실행할 수 있는지 테스트
      #!/bin/sh
      if [ -f file ]
      then
          echo "file exists"
      else
          echo "file not found"
      fi
      if [ -d file ]
      then
          echo "file is a directory"
      else
          echo "file is not a directory"
      fi
      if [ -s file ]
      then
          echo "file is not of zero length"
      else
          echo "file size is zero length"
      fi
      if [ -r file -a -w file ]
      then
          echo "file is readable and writable"
      else
          echo "file is not read/write"
      fi
    
  • 함수

      #!/bin/sh
      lister(){
          echo "현재 디렉터리는 'pwd'입니다"
          echo "현재 디렉터리 파일은 아래와 같습니다"
          ls
      }
      lister
    

배쉬 쉘 문법과 구조

  • 대부분의 리눅스에서 기본 쉘로 bash(Bourne Again SHell) 쉘을 사용하고 있음
  • 로그인 과정
    1. init 프로세스 생성
    2. getty 프로세스 생성
    3. 로그인 프롬포트를 보여줌
    4. /bin/login 프로그램이 실행되고 로그인 아이디를 입력하면 로그인 패스워드 입력을 기다림
    5. 입력받은 패스워드가 정확하면 환경 설정이 초기화되고 로그인 쉘을 시작
    6. /etc/profile 시스템 파일을 찾아 명령 라인에서 실행
    7. 유저의 홈디렉터리에 있는 유저의 초기 파일인 .bash_profile 파일 실행, .bashrc 라는 환경 파일을 실행
    8. 달러($) 기호를 모니터에 보여주며 유저의 명령을 기다림

배시 쉘 옵션 설정을 위한 set, shopt 명령

  • set 명령어는 특수한 빌트인 내장 옵션들을 켜고, 끄는 역할을 함
  • set -o : 쉘의 설정값 출력
  • set -o option : 옵션을 사용, set +o option : 옵션을 사용하지 않음
  • set -[a-z] : 옵션을 사용하는 단축형
  • set +[a-z] : 옵션을 사용하지 않는 단축형

    • allexport
    • braceexpand
    • emacs
    • errexit
    • errtrace
    • functrace
    • hashall
    • histexpand
    • history
    • ignoreeof
    • interactive-comments
    • keyword
    • monitor
    • noclobber
    • noexec
    • noglob
    • nolog
    • notify : 백그라운드 job이 종료되었을 때 유저에게 알려줌
    • nounset
    • onecmd
    • physical
    • pipefail
    • posix
    • privileged
    • verbose
    • vi
    • xtrace
  • shopt 명령은 배시 쉘에서 사용되는 set 명령으로 새로운 버전의 쉘 옵션 내장명령어. -p 옵션을 사용해 모든 옵션을 출력해볼 수 있으며, -u 옵션을 사용해 옵션 셜정을 off할 수 있고, -s 옵션을 사용해 on할 수 있음

배열

  • 배시 쉘은 1차원 배열 생성을 지원. 배열은 하나의 변수 이름에 숫자 목록, 이름 목록, 파일 목록 등의 단어 목록 집합을 할당할 수 있음. 빌트인 함수 declear -x[0] 형태로 생성
  • 배열의 요소를 가져올 때에는 ${배열명[index]} 형식을 사용하며 -ar 옵션을 사용하면 읽기전용 배열이 생성
  • 배열 요소의 개수를 파악할 경우엔 ${#names[*]}을 사용
  • 배열의 인덱스를 사용해 특정 인덱스에만 값을 넣을 수 있음

함수

  • 쉘의 컨텍스트 안에 명령들의 그룹 이름을 사용한다(자식 프로세스가 생성되지 않음)
  • 함수가 한번 정의되면 쉘 메모리에 적재되기 때문에 함수가 호출될 때 디스크로부터 읽어들일 필요가 없다. 함수는 스크립트의 모듈을 향상시키기 위해 사용
  • 실행될 때 프롬프트에서 정의될 수 있지만 대부분 유저의 초기화 파일(.bash_profile)에 정의한다. 그리고 함수는 호출되기 이전에 반드시 정의되야 함

  • 함수 정의
    • 본 쉘 방식 : 함수명을 적고 빈 괄호를 적은 다음 함수 정의
    • function 키워드를 사용. 컬리 브레이스({) 앞뒤에는 반드시 공백이 있어야 함
    • 정의 부분은 세미콜론(;)으로 분리된 명령어들로 구성되며, 마지막 명령어는 세미 콜론(;)으로 끝나야 함
  • declare -F 명령을 실행하면 정의되어 있는 함수명을 출력할 수 있으며, unset -f 함수명을 사용해 현재 쉘에서 정의한 함수를 제거할 수 있다

exec 명령

  • 새로운 프로세스를 시작하지 않고 현재 프로그램을 대신하여 사용될 수 있다. 표준 출력 또는 표준 입력은 서브쉘을 생성하지 않고 exec 명령으로 변경될 수 있다. 만약 파일이 exec 명령으로 오픈되면 파일의 끝까지 한 라인씩 읽게 된다

파이프

  • 파이프 심볼(|)의 왼쪽 명령의 결과를 가져와서 오른쪽 명령의 입력으로 사용하도록 함
  • who | wc -l
    • 파이프는 3단계의 처리를 수행
    • who 명령의 결과값이 커널 버퍼로 보내지고, wc -l 명령은 버퍼로부터 데이터를 읽은 다음, 결과를 모니터 스크린으로 보냄

카일스쿨 유튜브 채널을 만들었습니다. 데이터 사이언스, 성장, 리더십, BigQuery 등을 이야기할 예정이니, 관심 있으시면 구독 부탁드립니다 :)

PM을 위한 데이터 리터러시 강의를 만들었습니다. 문제 정의, 지표, 실험 설계, 문화 만들기, 로그 설계, 회고 등을 담은 강의입니다

이 글이 도움이 되셨거나 다양한 의견이 있다면 댓글 부탁드립니다 :)

Buy me a coffeeBuy me a coffee





© 2017. by Seongyun Byeon

Powered by zzsza