트랙
/
Bash
Bash
/
학습 과정
/
명령과 인자
명령

명령과 인자 에서 Bash

{other: "%{count}개의 연습 문제"}

명령과 인자 소개

셸

_셸_은 명령을 입력해 운영체제와 상호 작용할 수 있게 해 주는 프로그램이에요. 셸은 그래픽 방식일 수도 있어요. 마우스나 트랙패드로 사용하는 데스크톱 환경도 셸이에요. 여기서는 명령줄 인터페이스에 집중해요. 명령을 입력하면 그 결과가 (보통은) 터미널에 출력되는 방식이죠.

명령줄 셸에는 여러 가지가 있어요. Windows에는 cmd와 powershell이 있어요. Mac과 Linux에는 "유닉스 셸"이 있는데, 그 종류도 아주 많아요. Mac의 기본 셸은 zsh예요. csh는 C와 비슷한 문법을 가진 예전 셸이에요. bash는 특히 Linux에서 가장 널리 쓰이는 셸 중 하나예요.

가장 초기의 셸 중 하나는 Bourne 셸인 sh예요. 시간이 지나면서 이 셸은 표준화되어 모든 유닉스 계열 시스템에서 제공되게 되었어요. 많은 셸이 Bourne 셸을 바탕으로 만들어졌고, 대개 대화형 기능이나 프로그래밍 기능을 더 제공해요.

Bash는 Bourne 셸을 바탕으로 만들어졌어요. 그래서 "Bourne-again shell"이라고 불려요. 많은 Linux 시스템에서 기본 셸로 쓰여요. Bash는 대화형 셸로 쓰기 즐겁게 만들어 주는 여러 기능을 제공해요. 예를 들면:

  • 완전히 바꿀 수 있는 프롬프트,
  • 기록에서 명령을 다시 불러오고 수정하는 기능,
  • 실행 중인 프로세스를 다루는 기능,
  • 입력을 줄여 주는 별칭,
  • 그 밖에도 여러 가지.

이 트랙은 bash를 대화형 셸로 사용하는 방법에 관한 게 아니에요. bash로 프로그램을 작성하는 법을 배우는 트랙이에요. 문제를 풀 때 오직 bash만 쓰는 데 중점을 두지만, 외부 도구를 불러 쓰는 걸 제한하지는 않아요.

Bash 기초: 명령과 인자

Bash는 입력(보통 터미널이나 파일)에서 명령을 읽어요. 읽어 들인 각 줄은 명령, 즉 실행할 지시로 취급돼요. (여러 줄에 걸친 명령처럼 좀 더 고급스러운 경우도 있는데, 이건 나중에 다뤄요.)

Bash는 각 줄을 공백 문자(스페이스와 탭)로 구분된 단어로 나눠요. 줄의 첫 번째 단어가 실행할 _명령_의 이름이에요. 나머지 단어는 모두 그 명령의 인자(옵션, 파일 이름 등)가 돼요.

Note

아래 예제에서는 직접 실행해 볼 수 있는 대화형 명령 몇 가지를 보여 드릴게요.

  • # 문자는 _주석_을 시작해요. 주석 뒤에 오는 단어는 셸이 모두 무시해요. 주석은 읽기 위한 것일 뿐이에요.
  • $ 문자는 셸 프롬프트를 나타내요. 명령을 복사할 때는 프롬프트는 포함하지 마세요.
# Note: It is always a good idea to run `ls` before `rm`
# to make sure you know what you are deleting.

$ rm *            # Remove all files in the current directory.
                  # Make sure you run this in a temp directory or you can lose files!
$ ls              # List files in the current directory (no output, no files).
$ touch a   b c   # Create files 'a', 'b' and 'c'.
$ ls              # List files again; this time the outputs: 'a', 'b' and 'c'.
a  b  c

rm은 넘겨받은 파일을 삭제하는 애플리케이션이에요. *는 glob 문자로, 현재 디렉터리의 모든 파일과 일치하는 "와일드카드" 패턴이에요. glob은 나중에 더 자세히 이야기해요.

a와 b 사이에는 스페이스가 여러 개 있고 b와 c 사이에는 하나뿐이라는 걸 눈치챘나요? 인자 사이의 공백 양은 상관없어요! 예를 들어:

$ echo This is a test.
This is a test.
$ echo This    is    a    test.
This is a test.

echo는 인자를 표준 출력(여기서는 터미널)에 출력하는 명령이에요. Bash는 그 명령을 다섯 개의 단어로 나누고, echo 명령을 네 개의 인자와 함께 실행해요. 두 번째 예제의 여분의 스페이스는 아무 차이도 만들지 않아요. echo는 단어 사이에 원래 스페이스가 어떻게 있었는지 알지 못해요.

여분의 공백을 그대로 유지하고 싶다면, 문장을 하나의 인자로 넘겨야 해요. 따옴표를 사용하면 돼요:

$ echo "This    is    a    test."
This    is    a    test.

따옴표는 안에 있는 모든 것을 _하나의 인자_로 묶어요. 이때 인자는 그 특정한 스페이스 문자들을 포함한 This is a test.가 돼요. 따옴표 자체는 인자의 일부가 아니에요. bash는 그 하나의 인자를 echo에 넘기기 전에 따옴표를 없애요.

인자는 명령 이름과, 그리고 서로 간에 공백으로 구분돼요. 이 점은 꼭 기억해 둬야 해요. 예를 들어, 다음은 잘못된 예예요:

$ [-f file]
bash: [-f: command not found

file이라는 파일이 있는지 확인하려는 의도예요. [와 ] 주위의 공백은 상관없다고 생각하기 쉬워요. 하지만 [는 실제로 명령이고, 마지막 인자가 ]여야 해요. ([ 명령은 나중에 더 자세히 다뤄요.) 따라서 [는 -f와, ]는 file과 떼어 놓아야 해요. 그러지 않으면 bash는 [-f라는 명령을 인자 file] 하나와 함께 실행하려 한다고 생각해요. 올바른 명령은 모든 인자를 공백으로 구분해요:

$ [ -f file ]

(이 동작 때문에 헷갈려 하는 분을 많이 봐요. [...]는 "그냥 문법"이라서 공백을 빼도 된다고 생각하죠. 하지만 [는 명령이고 공백이 꼭 필요해요.)

파일 이름에 공백이나 다른 특수 문자가 들어 있다면, 그것도 따옴표로 묶어야 해요:

$ [ -f "my file" ]

명령의 종류

Bash는 여러 종류의 명령을 이해해요. 별칭, 함수, 내장 명령, 키워드, 실행 파일이에요. 이들은 모두 _명령_이고, 아주 비슷하게 동작해요.

기본적으로 별칭은 대화형 셸에서만 쓸 수 있고, 셸 스크립트에서는 쓸 수 없어요. 여기서는 다루지 않아요.

함수

함수는 셸 명령을 담고 있고, 작은 스크립트처럼 동작해요. 인자를 받거나 지역 변수를 만들 수도 있어요. 함수를 호출하면 그 안의 명령이 실행돼요. 함수는 나중에 자세히 다뤄요.

내장 명령

Bash에는 기본 명령 몇 가지가 내장돼 있어요. cd(디렉터리 변경), echo(출력 쓰기) 등이에요. 이미 제공된 함수라고 생각하면 돼요.

키워드

키워드는 내장 명령과 비슷한데, 가장 큰 차이는 키워드가 실제로 bash 문법이고 특별한 규칙으로 해석될 수 있다는 점이에요. 예를 들어 [는 bash 내장 명령이고 [[는 bash 키워드예요. 둘 다 여러 조건을 검사할 때 써요. 그런데 [[ 키워드에는 몇 가지 추가 기능이 있어요. 예를 들어 정규 표현식 검사를 하는 =~ 연산자를 제공해요.

$ [[ "hello world" =~ e.*o ]]   # returns a "success" status
$ [ "hello world" =~ e.*o ]
bash: [: =~: binary operator expected

문자열 비교 연산자 <와 >도 키워드가 코드를 다르게 해석한다는 걸 보여 주는 또 다른 예예요. [[...]] 안에서는 이 연산자들을 특별히 신경 쓸 필요가 없어요.

$ if [[ "$USER" < "n" ]]; then
    echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet

하지만 [...] 안에서는 셸이 이들을 리다이렉션 연산자로 해석하지 않도록 따옴표로 묶거나 이스케이프해야 해요.

$ if [ "$USER" < "n" ]; then
    echo "$USER is in the first half of the alphabet"
fi
# => bash: n: No such file or directory

$ if [ "$USER" \< "n" ]; then
    echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet

실행 파일

bash가 실행할 수 있는 마지막 종류의 명령은 _실행 파일_이에요. 컴퓨터의 파일 시스템에 있는, 실행할 수 있는 파일(프로그램)이에요. (실행 파일은 외부 명령 또는 _애플리케이션_이라고도 해요.) 실행 파일은 보통 이름만 입력해서 실행해요. 파일의 전체 경로는 대개 필요하지 않아요. 이게 가능한 이유는 bash가 미리 정의된 변수를 사용해 일반적인 실행 파일의 위치를 찾기 때문이에요. 이 변수의 이름은 PATH예요. 콜론으로 구분된 디렉터리 이름들의 집합이에요(예: /usr/bin:/bin). 파일 경로 없이 명령(예: myprogram 또는 ls)을 지정하면(그리고 그것이 별칭, 함수, 내장 명령, 키워드가 아니라면), bash는 PATH의 디렉터리들을 차례로 검색해요. 디렉터리를 왼쪽에서 오른쪽으로 순서대로 검색하면서, 주어진 이름의 실행 파일이 있는지 확인해요.

실행 파일이 알려진 경로 밖에 있다면, 실행 파일의 경로를 지정해야 해요. 현재 디렉터리에 있는 myprogram 실행 파일이라면 ./myprogram을 쓰고, /opt/somedirectory 디렉터리에 있다면 /opt/somedirectory/myprogram을 써요.

팁: type 명령을 사용하면 그 명령에 대한 자세한 정보를 알 수 있어요:

$ type rm
rm is hashed (/bin/rm)
# or it might return: `rm is /bin/rm`

$ type cd
cd is a shell builtin

스크립트

_스크립트_는 파일에 저장된 일련의 명령이에요. Bash는 파일을 읽고 명령을 순서대로 처리해요. 현재 명령이 끝나야 다음 명령으로 넘어가요. 예외는 명령이 비동기적으로(백그라운드에서) 실행되도록 지정된 경우예요. 이 경우는 아직 너무 걱정하지 않아도 돼요. 나중에 어떻게 동작하는지 배워요.

이 안내서에 나오는 명령줄 예제는 모두 스크립트에서도 쓸 수 있어요.

스크립트를 만드는 건 쉬워요. 먼저 새 파일을 만들고 첫 줄에 이걸 넣어요:

#!/bin/bash

이 줄을 _인터프리터 지시문_이라고 해요(해시뱅 또는 **셰방**이라고도 해요). 이 파일이 명령의 실행 파일로 쓰일 때 /bin/bash를 인터프리터로 사용하라고 지정하는 거예요. 운영체제의 커널은 _바이너리가 아닌 파일_을 실행할 때 파일의 첫 줄을 읽어요. 그 줄이 #!로 시작하면, 커널은 그 줄을 보고 파일을 넘겨줄 인터프리터를 결정해요. (이걸 하는 다른 올바른 방법도 있어요. 아래를 봐요.) #!는 파일의 맨 앞에 있어야 하고, 앞에 공백이나 빈 줄이 있으면 안 돼요. 우리 스크립트의 명령은 이 줄 아래에 각각 다른 줄로 나와요.

팁: #!/bin/bash 대신 이렇게 쓸 수도 있어요

#!/usr/bin/env bash

env는 첫 번째 인자로 주어진 이름의 실행 파일을 $PATH에서 찾아요(이 경우에는 "bash"). 이 기법과 평범한 #!/bin/bash와의 차이에 대한 더 자세한 설명은 StackOverflow의 이 질문을 참고해요.

인터넷에서 인터프리터로 /bin/sh를 사용하는 스크립트나 예제에 속지 마세요. sh는 bash가 아니에요! bash 자체는 "sh 호환" 셸이라서 대부분의 sh 스크립트를 실행할 수 있고 문법도 상당 부분 공유해요. 하지만 그 반대는 성립하지 않아요. bash의 일부 기능은 sh에서 깨지거나 예상치 못한 동작을 일으켜요. 사실 단 하나의 sh 셸이라는 건 없어요. POSIX-sh 문법을 지원하는 셸 구현이 아주 많아요. 운영체제에 따라 sh를 실행했을 때 실제로는 bash, ash, dash 또는 다른 셸이 실행될 수도 있어요.

스크립트 파일을 만들었으면 이렇게 실행할 수 있어요:

$ bash myscript

이 예제에서는 명령 bash를 실행하면서, _인자_로 넘긴 스크립트를 읽으라고 알려 줘요. 이렇게 실행하면 #! 줄은 그냥 _주석_일 뿐이에요.

또는 스크립트에 실행 권한을 줄 수도 있어요. 이 방법을 쓰면 bash를 직접 호출하지 않고도 다른 실행 파일처럼 스크립트를 실행할 수 있어요:

$ chmod +x myscript  # Mark the file as executable.
$ ./myscript         # Now, myscript can be executed directly
                     # instead of having to specify bash.

이렇게 명령을 실행하면 운영체제(OS)가 #! 줄을 보고 스크립트를 실행할 인터프리터를 결정해요.

출처

이 수업의 내용은 Bash Guide의 명령과 인자에서 가져왔어요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요