>는 사실 1>의 줄임이다. 즉 ls > out.txt와 ls 1> out.txt는 완전히 같다.
⚠️ 함정: >는 명령 실행 전에 파일을 먼저 비운다(truncate). 그래서 sort file.txt > file.txt처럼 입력과 출력이 같은 파일이면 sort가 읽기도 전에 파일이 비워져 내용이 통째로 날아간다. 같은 파일에 결과를 쓰려면 임시 파일을 거치거나 sort -o file.txt file.txt처럼 도구의 자체 옵션을 써야 한다.
둘을 같은 파일로 모으려면 FD 복제 표기 2>&1을 쓴다. "2번을 1번이 가리키는 곳과 같게 만들어라"는 뜻이다.
command > output.txt 2>&1 # stdout과 stderr 모두 output.txt로command &> output.txt # 위와 동일 (bash 단축 표기)command >> output.txt 2>&1 # 이어쓰기로 둘 다
⚠️ 함정: 2>&1의 위치(순서)가 결정적이다. command 2>&1 > out.txt는 의도와 다르게 동작한다. 셸은 왼쪽부터 처리하므로, 먼저 2>&1이 "2번을 현재 1번(화면)과 같게" 만들고, 그 뒤에> out.txt가 1번만 파일로 바꾼다. 결과적으로 stderr는 화면에 남고 stdout만 파일로 간다. 반드시 > out.txt 2>&1 순서로 써서 1번을 먼저 파일로 돌린 뒤 2번을 1번에 합쳐야 한다.
sort < names.txt # names.txt의 내용을 sort의 입력으로wc -l < access.log # 파일을 입력으로 줄 수 세기mail user@host < body.txt # 파일 내용을 메일 본문으로
여러 줄을 즉석에서 입력으로 넘기는 히어 도큐먼트(here document, <<)도 있다.
cat << END첫 번째 줄두 번째 줄END# END를 만날 때까지의 텍스트를 cat의 입력으로 전달
<<(히어 도큐먼트)와 비슷한 <<<(히어 스트링, here string)도 있다. <<<는 한 줄 문자열을 그대로 stdin으로 넘긴다. 예를 들어 grep "root" <<< "$line"은 변수 $line의 내용을 grep의 입력으로 준다. 임시 파일이나 echo | grep 파이프 없이도 문자열을 명령에 먹일 수 있어 스크립트에서 유용하다.
파이프로 연결된 명령들은 동시에(병렬로) 실행되며, 데이터는 커널이 관리하는 파이프 버퍼를 통해 흐른다.
ls -l | grep ".txt" # 목록에서 .txt만 추리기cat access.log | grep "404" | wc -l # 404 응답 줄 수 세기ps aux | grep nginx # 실행 중 nginx 프로세스 찾기du -h | sort -rh | head -5 # 용량 큰 항목 상위 5개
파이프는 stdout만 다음으로 넘긴다. stderr(2번)는 파이프를 타지 않고 화면에 남는다. stderr까지 파이프로 넘기려면 command 2>&1 | next 또는 bash의 command |& next 표기를 쓴다.
명령을 실행하기 전에 셸은 입력 줄에서 특수한 의미를 가진 문자(메타문자, metacharacter)를 먼저 처리한다.
그중 파일 이름을 매칭하는 데 쓰는 것을 와일드카드(wildcard) 또는 글로빙(globbing)이라 한다.
와일드카드
의미
예시
*
0개 이상의 임의 문자
*.txt → 모든 .txt
?
정확히 1개의 임의 문자
file?.log → file1.log, fileA.log
[ ]
괄호 안 문자 중 하나
file[123].txt
[a-z]
범위 안의 한 문자
[a-c]* → a,b,c로 시작
[!abc]
괄호 안 문자를 제외한 하나
[!0-9]* → 숫자로 시작 안 함
{ }
중괄호 확장(brace)
file{1,2,3}.txt
ls *.txt # 확장자가 .txt인 모든 파일rm temp?.log # temp 뒤에 한 글자 + .logls [a-c]* # a, b, c로 시작하는 모든 파일cp file{1,2,3}.txt /backup/ # file1.txt file2.txt file3.txt 복사ls *[!0-9] # 마지막 글자가 숫자가 아닌 파일
와일드카드 확장(globbing)을 수행하는 주체는 명령이 아니라 셸이다. ls *.txt를 입력하면 셸이 먼저 *.txt를 실제 파일 이름들(a.txt b.txt c.txt)로 바꾼 뒤, 그 결과를 ls에게 인자로 넘긴다. 그래서 매칭되는 파일이 하나도 없으면 bash 기본 동작에서는 *.txt가 글자 그대로 ls에게 전달된다. 이 "셸이 먼저 확장한다"는 점은 정규표현식과 와일드카드의 결정적 차이로 이어진다.
와일드카드(글로빙)와 정규표현식(regex)을 혼동하면 안 된다. 둘 다 *를 쓰지만 의미가 다르다. 글로빙에서 *는 "임의의 0개 이상 문자"지만, 정규표현식에서 *는 "바로 앞 문자의 0회 이상 반복"이다. 예를 들어 글로빙 a*는 a로 시작하는 모든 것이지만, regex a*는 a가 0번 이상(즉 빈 문자열 포함)을 뜻한다. grep의 패턴은 정규표현식, 파일 이름 매칭은 글로빙이다.
메타문자를 글자 그대로 쓰고 싶거나 변수 확장을 제어하려면 따옴표를 쓴다. 세 종류의 동작이 모두 다르다.
따옴표
이름
변수($) 확장
명령 치환(`)
와일드카드
' '
작은따옴표(strong)
❌
❌
❌
" "
큰따옴표(weak)
✅
✅
❌
` `
역따옴표(backtick)
—
명령 실행
—
NAME="linux"echo '$NAME' # $NAME (작은따옴표: 전혀 확장 안 함)echo "$NAME" # linux (큰따옴표: 변수만 확장)echo `date` # 현재 날짜 (역따옴표: 명령 실행 결과로 치환)
작은따옴표('): 안의 모든 것을 문자 그대로 취급한다. $, *, 공백 모두 글자다.
큰따옴표("): 변수($VAR)와 명령 치환(`cmd`)은 확장하되, 와일드카드는 막는다. 공백을 포함한 값을 안전하게 다룰 때 필수다.
역따옴표(`): 안의 명령을 실행해 그 출력으로 치환한다(명령 치환). 현대 bash에서는 $(...) 표기를 더 권장한다.
# 명령 치환 — 두 표기는 동일, $() 가 중첩에 안전echo "오늘은 `date +%Y-%m-%d` 입니다"echo "오늘은 $(date +%Y-%m-%d) 입니다"# 변수 값에 공백이 있을 때 큰따옴표의 중요성FILE="my report.txt"rm "$FILE" # 올바름: 'my report.txt' 한 파일 삭제rm $FILE # 위험: 'my'와 'report.txt' 두 인자로 분리됨
큰따옴표로 변수를 감싸야 하는 이유는 단어 분리(word splitting) 때문이다. 따옴표 없이 변수를 쓰면 셸이 값 안의 공백을 기준으로 여러 인자로 쪼갠다. 그래서 스크립트에서는 거의 항상 "$VAR"처럼 큰따옴표로 감싸는 것이 안전한 관용구다.
역따옴표 명령 치환의 가장 큰 약점은 중첩이다. `cmd1 `cmd2` `처럼 역따옴표를 겹치면 어디서 끝나는지 셸이 헷갈린다. 1990년대 POSIX가 $(...) 문법을 도입한 가장 큰 이유가 이 중첩 문제 해결이었다. $(cmd1 $(cmd2))처럼 괄호로 명확히 중첩되므로 오늘날 스크립트에서는 $()가 표준이다.