리눅스는 각 명령어가 가능한 한 작고 간단한 동작만을 수행하도록 설계되었다.
한 명령어에 다양한 기능을 포함하기보다는 적은 기능을 포함하는 것이 알기 쉽고 사용하기에도 쉽기 때문이다. 명령어들을 조합하는 두 가지 방법인 리다이렉션과 파이프라인을 알아보자.
명령어 간에 실행 결과를 주고받는 원리를 알아본다.
리눅스에는 cat
과 같은 명령어를 실행하면 자동으로 표준 입출력 채널이 열린다.
채널은 데이터가 흐르는 길이다.
표준 입출력의 세 가지
- 표준 입력(stdin) : 프로그램에 데이터를 입력하는 채널. 키보드를 통한 입력이 대표적인 예이다.
- 표준 출력(stdout) : 프로그램의 실행 결과가 출력되는 채널. 기본값으로 단말 디스플레이에 출력한다.
- 표준 에러 출력(stderr) : 프로그램 실행 중 발생하는 에러 메시지가 출력되는 채널. 보통 표준 출력과 동일하게 단말 디스플레이에 출력한다.
명령어의 입장에서는 단순히 표준 입력을 읽어서 결과를 표준 출력으로 출력할 뿐, 표준 입력은 키보드로 입력될 수도, 파일이 될 수도 있고, 표준 출력은 모니터, 프린터 혹은 파일이 될 수도 있다.
리눅스에서는 명령어의 입출력을 표준 입출력으로 추상화하고 있기에 명령어의 내부에서는 실제 입출력이 어디에 연결되었는지 신경 쓰지 않으며 사용자가 명령어를 실행할 때 이를 자유롭게 지정할 수도 있다.
표준 입출력을 어디로 연결할지 변경하는 것을 뜻한다.
이전에 cat
명령어에 인자를 지정하지 않으면 키보드로 입력될 때까지 커서가 정지된다 했었다.
이는 cat
명령어가 표준 입력으로 입력한 내용을 읽어서 그대로 표준 출력에 출력하는 명령어이기 때문이다. 표준 입력은 보통 키보드 입력이므로 키보드 입력을 기다렸다가 그대로 출력한다.
키보드 대신 파일을 표준 입력으로 연결한다면?
이를 입력 리다이렉션이라고 하며 <
기호를 사용한다.
/etc/crontab
파일을 표준 입력으로 연결해보자.
인자로 파일명을 지정했을 때와 출력 결과는 같지만, 내부적인 차이점이 있다.
입력 리다이렉션을 사용한 경우는 표준 입력을 읽어서 표준 출력에 그대로 출력한다
는 cat
명령어의 기본 동작에 충실한 방식이고, 인자로 파일명을 지정하는 방식은 cat
명령어가 사용자의 편의를 위해 특별히 제공하는 방식을 사용한 것이다.
만약 우리가 리눅스 명령어를 만들게 된다면 표준 입력을 읽어 들이는 방식을 반드시 지원하도록 구현하는 것이 좋다. 그래야 다른 프로그램과 연동하여 더 강력한 기능 수행이 가능할 것이다.
표준 출력도 리다이렉션 할 수 있다.
자주 사용되는 패턴은 명령어의 실행 결과를 화면에 출력하는 것이 아니라 파일에 저장하는 것이다. 마찬가지로 >
기호를 사용한다.
다음과 같은 ls -l /
명령어의 실행 결과를 list.txt
파일에 저장하는 예를 확인해보면, 명령어의 실행 결과를 화면에 출력하는 것이 아닌 지정한 파일에 저장한다.
(리다이렉션된 파일은 touch
같은 명령어로 미리 만들지 않아도 자동 생성이 된다)
출력 채널에는 표준 출력 외에도 표준 에러 출력이라는 것이 있었다.
이는 프로그램의 에러 메시지를 출력하기 위해 사용되는데, ls
명령어로 존재하지 않는 파일을 지정할 때 에러 메시지를 표시하는데, 이러한 메시지는 표준 에러 출력에 해당된다.
존재않는 파일을 표준 출력으로 리다이렉트해보니 표준 에러 출력만 콘솔에 표시된다.
표준 출력을 파일에 리다이렉션했지만 표준 에러 출력은 별도로 리다이렉션하지 않아서 에러 메시지가 콘솔에 출력된 것. (표준 출력과 표준 에러 출력이 별도의 채널을 사용하기 때문)
표준 에러 출력도 파일에 리다이렉션 할 수 있는데, 이때는 2>
기호를 사용한다.
다음과 같이 명령어 실행 중 발생한 에러가 콘솔에 출력되지 않고 지정한 파일에 저장된다.
이처럼 명령어의 실행 결과와 에러 메시지를 별도로 다루려고 표준 출력과 표준 에러 출력이 별도로 존재한다.
ex) ls /xxxxx > list.txt 2> error.txt
를 실행하면?
콘솔에는 아무것도 출력되지 않으며, ls
명령어의 실행 결과는 list.txt
에 저장,
에러 메시지는 error.txt
에 저장된다.
표준 출력과 표준 에러 출력을 파일 하나에 리다이렉션 할 때는 표준 출력을 리다이렉션 한 뒤에 2>&1
을 붙여준다. 여기서 &1
은 표준 출력을 의미한다.
표준 에러 출력(2)를 표준 출력(1)과 같은 파일로 리다이렉션 한다는 의미이다.
표준 입력 : 0
표준 출력 : 1
표준 에러 출력 : 2
ls /xxxxx > result.txt 2>&1
이미 존재하는 파일에 표준 출력을 리다이렉션하면 기존 파일을 지우고 덮어쓴다.
따라서 리다이렉션으로 인해 중요한 파일을 덮어씌워 분실하는 경우가 종종 생긴다.
이를 방지하려면 >
대신에 >>
을 사용하면 덮어쓰지 않고 파일의 끝에 이어서 기록하게 된다.
또 다른 방법으로는 셸 옵션으로 noclobber
라는 값을 set
명령어로 지정할 수 있다. 그러면 리다이렉션으로 덮어쓸 때 에러를 발생시켜 이를 방지한다.
리다이렉션을 위한 기호와 의미
< FILE
: 표준 입력을 FILE로 변경
> FILE
: 표준 출력을 FILE로 변경
>> FILE
: 표준 출력의 출력을 FILE의 끝에 추가
2> FILE
: 표준 에러 출력을 FILE로 변경
2>> FILE
: 표준 에러 출력을 FILE의 끝에 추가
> FILE 2>&1
: 표준 출력과 표준 에러 출력을 FILE로 변경
리눅스를 사용하다 보면 종종 /dev/null
이라는 파일에 리다이렉션 할 때가 있다.
/dev/null
파일은 특수 파일로서 다음 특성을 가진다.
다음과 같이 표준 출력으로 출력되는 양이 너무 많거나 표준 에러 출력만 확인할 때 자주 사용하는 방식이다. 굳이 출력되는 메시지를 확인하지 않아도 되거나 명령어의 실행 시간을 확인할 때 이 방식을 사용한다.
여러 명령어를 연결하려면 한 명령어의 실행 결과를 다른 명령어에 입력할 수 있어야 한다.
이를 위해 존재하는 기능이 파이프라인이다. 이를 사용하면 명령어의 표준 출력을 다른 명령어의 표준 입력으로 연결할 수 있다.
ls
명령어의 실행 결과가 너무 큰 경우를 생각해보자.
다음과 같이 실행 결과를 파일에 리다이렉션한 뒤에 less
명령어로 해당 파일을 조회하는 것도 한 방법이다.
하지만 매번 실행 결과를 파일에 저장하는 방식은 비효율적이다.
중간에 파일을 만들지 않고 파이프라인 (|)
을 사용하여 첫 번째 명령어의 표준 출력을 두 번째 명령어의 표준 입력으로 전달한다. 즉, ls
명령어의 결과를 less
명령어에 전달한 것.
표준 출력으로 결과를 출력하는 모든 명령어를 파이프로 연결할 수 있다. 주로 명령어의 실행 결과가 방대할 때 자주 사용하는 패턴이다.
~/.bash_history
라는 파일에 기록되는데, 이를 커맨드 라인 이력이라고 하며 history
명령어로 출력이 가능하다.less
와 연동하면 페이지 단위로 읽을 수 있게 된다.history | less
실행ls -l /etc | cat -n | less
실행ls
명령어의 결과를 cat
명령어로 행 번호를 붙여 출력하고 그 결과를 다시 less
로 한 페이지씩 표시하고 있다.ls -l / /xxxxx 2>&1 | less
실행지금까지 사용한 cat
명령어처럼 표준 입력을 받아들여 표준 출력으로 출력하는 명령어를 필터 명령어라 한다.
head
명령어는 지정한 파일의 첫 행부터 지정한 행까지 출력한다.
이때 출력할 행 수를 지정하지 않으면 첫 10행을 지정한다.
기본적으로 필터 명령어는 파이프라인과 함께 많이 사용된다.
history | head
cat
: 입력 내용을 그대로 출력
head
: 파일 앞부분을 출력
tail
: 파일 뒷부분을 출력
grep
: 검색 패턴에 일치하는 행을 출력
sort
: 정렬
uniq
: 중복된 행을 제거하여 출력
tac
: 역순으로 출력
wc
: 행 수나 바이트 수를 출력
필터 명령어는 파이프라인을 사용하여 다른 명령어와 조합할 때 그 진가가 발휘된다.
du
명령어는 파일이나 디렉터리의 크기를 출력한다. -b
옵션은 바이트 단위 출력이다.
/bin
디렉터리에 있는 파일을 크기가 큰 순서대로 출력하는 예를 들어보자.
이번에는 파일 크기가 큰 순으로 출력해보자. sort
에 -r
옵션으로 반대 정렬이 가능하지만 tac
명령어 (역순으로 출력)를 사용해본다.
파일 크기가 큰 상위 5개의 파일만 출력