리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.

리눅스 시스템 기초 27 / 50 · Part 3. Linux 명령어 활용
실습 환경: Rocky Linux 9 (10.0.0.200) · 21편 실습용 샘플 로그
이전 글: 26. sed 활용

1. 들어가며

21~26편에서 grep … | sort | uniq -c | sort -rn, 2>/dev/null, > 결과.txt를 계속 썼다. 이번 글은 이 기호들이 실제로 무엇을 연결하는지 정리한다.

파이프와 리다이렉션을 정확히 이해해야 하는 이유는 세 가지다.

  1. 오류를 놓치지 않기 위해: 파이프는 오류(stderr)를 다음 명령으로 넘기지 않고, 종료 코드도 기본적으로 마지막 명령 것만 남는다. 분석 스크립트가 실패했는데 성공한 것처럼 보일 수 있다.
  2. 결과를 안전하게 남기기 위해: >는 파일을 덮어쓴다. 순서 하나 틀리면 기존 결과나 원본을 날린다.
  3. 분석 과정을 기록하기 위해: tee, script로 화면에 본 것과 같은 내용을 파일로 남겨야 보고서의 근거가 된다.

2. 핵심 개념

2-1. 파일 디스크립터 0·1·2

번호이름기본 연결용도
0stdin키보드입력
1stdout화면정상 출력
2stderr화면오류·경고 메시지

4편의 /proc/<PID>/fd에서 본 번호가 바로 이것이다. 프로그램이 파일이나 소켓을 더 열면 3번부터 번호가 붙는다.

2-2. 리다이렉션

표기의미
> filestdout을 파일로 (덮어쓰기)
>> filestdout을 파일 끝에 추가
2> filestderr를 파일로
2>/dev/nullstderr 버리기
> file 2>&1stdout과 stderr 모두 파일로
&> file위와 같음 (bash)
< file파일을 stdin으로
<<EOF … EOF여러 줄을 stdin으로 (here-document)

2-3. 파이프와 관련 도구

표기 / 도구의미
`AB`
`A& B`
tee file입력을 화면(stdout)과 파일에 동시에
tee -a file파일에 추가
<(명령)명령 결과를 파일처럼 사용 (process substitution)
xargsstdin의 줄을 다른 명령의 인자 로
set -o pipefail파이프 중 하나라도 실패하면 전체 실패
${PIPESTATUS[@]}파이프 각 단계의 종료 코드

3. 동작 원리

표준 입출력(0·1·2)과 파이프 · 리다이렉션

3-1. 파이프는 동시에 실행된다

grep | sort | uniq는 grep이 끝난 뒤 sort가 시작되는 구조가 아니다. 쉘이 세 프로세스를 동시에 만들고, 커널의 파이프 버퍼로 grep의 1번 출력을 sort의 0번 입력에 연결한다. 그래서 ps로 보면 파이프의 모든 명령이 함께 떠 있다.

3-2. 리다이렉션은 왼쪽부터 처리된다

2>&1은 "2번을 지금 1번이 가리키는 곳으로 복사"라는 뜻이다. 그래서 순서에 따라 결과가 달라진다. 아래는 실제로 실행한 결과다.

$ ls /etc/hostname /nope > out1.txt 2>&1
$ cat out1.txt
ls: cannot access '/nope': No such file or directory
/etc/hostname

$ ls /etc/hostname /nope 2>&1 > out2.txt
ls: cannot access '/nope': No such file or directory      ← 화면에 출력됨
$ cat out2.txt
/etc/hostname
  • 첫 번째: 1번을 파일로 바꾼 뒤 2번을 1번에 복사 → 둘 다 파일
  • 두 번째: 2번을 1번(아직 화면)에 복사한 뒤 1번만 파일로 → 오류는 화면

3-3. 파이프의 종료 코드

쉘은 기본적으로 파이프의 마지막 명령 의 종료 코드만 $?에 남긴다.

$ grep zzz /nope | sort; echo "exit=$?"
grep: /nope: No such file or directory
exit=0                                    ← grep 실패를 놓침

$ set -o pipefail; grep zzz /nope | sort; echo "exit=$?"
grep: /nope: No such file or directory
exit=2

$ false | true | true; echo ${PIPESTATUS[@]}
1 0 0

로그 파일 경로가 틀려 grep이 실패했는데도 결과가 "0건"으로 보고되는 사고를 막으려면, 분석 스크립트에는 set -o pipefail 을 넣는다(28편).


4. 실습

cd ~/lab

# 1) 결과 저장 — 덮어쓰기 vs 추가
grep -c "Failed password" secure > report.txt
grep -c "Accepted" secure >> report.txt
cat report.txt

# 2) 권한 오류는 버리고 결과만
find / -xdev -name authorized_keys 2>/dev/null

# 3) 결과와 오류를 따로 저장
sudo find / -xdev -perm -4000 -type f > suid.txt 2> suid_err.txt
wc -l suid.txt suid_err.txt

# 4) 화면에 보면서 동시에 파일로 기록
grep "Accepted" secure | tee accepted.txt | wc -l

# 5) 두 명령 결과를 파일 없이 비교 (process substitution)
#    comm -12: 양쪽에 모두 있는 줄만
comm -12 <(grep -oE "from [0-9.]+" secure | sort -u) \
         <(awk '{print "from "$1}' access_log | sort -u)

# 6) stdin 줄을 인자로 — 의심 파일 해시 일괄 계산
printf '/usr/bin/ls\n/usr/bin/cat\n' | xargs sha256sum

# 7) 여러 줄 입력 (here-document)
cat > ioc.txt <<EOF
10.0.0.128
203.0.113.7
EOF
grep -F -f ioc.txt secure | wc -l

# 8) 분석 세션 전체 기록
script -q analysis_$(date +%F).log      # 이후 입력·출력 모두 기록, exit 로 종료

5. 결과 분석

샘플 로그에 실행한 결과다.

① report.txt

113
4

첫 줄은 >로 만들고 두 번째 줄은 >>로 추가했다. 두 번째에도 >를 썼다면 113은 사라진다.

② process substitution 비교

from 10.0.0.10
from 10.0.0.128

SSH 로그의 출발지 목록과 웹 로그의 출발지 목록을 임시 파일 없이 비교했다. 양쪽에 모두 있는 IP 는 SSH와 웹을 모두 사용한 출발지다. 10.0.0.10은 관리자 PC, 10.0.0.128은 공격 출발지다. 공격 IP가 두 서비스에 모두 나타난다는 것은 다단계 공격을 의심할 근거가 된다.


6. 보안 관점

실수결과예방
>로 원본 경로에 출력원본 로그를 빈 파일로 덮어씀 (grep x secure > secure)결과는 항상 별도 디렉터리, set -o noclobber
2>/dev/null 남용권한 부족으로 검색 못 한 영역을 모른 채 "없음" 판단오류를 별도 파일로 저장해 확인
파이프 종료 코드 무시입력 파일 오류를 "0건"으로 보고set -o pipefail, PIPESTATUS
분석 과정 미기록결과의 근거를 재현할 수 없음tee, script, 명령과 결과를 함께 저장
공격자의 출력 숨기기> /dev/null 2>&1로 악성 명령 출력 은닉출력이 아닌 실행 자체 를 기록 (auditd execve)

set -o noclobber를 켜면 >가 기존 파일을 덮어쓰지 않고 오류를 낸다. 덮어써야 할 때는 >|를 명시한다. 분석 작업용 셸에서 켜 두면 실수를 줄일 수 있다.


7. SOC / 보안관제 활용

7-1. 분석 결과 기록 패턴

set -o pipefail
set -o noclobber
OUT=~/case_$(date +%Y%m%d_%H%M); mkdir -p "$OUT"

# 명령과 결과를 함께 남기기
{
  echo "### $(date -Is) grep Accepted"
  grep "Accepted" /var/log/secure
} 2>&1 | tee "$OUT/01_accepted.txt"

sha256sum "$OUT"/*.txt > "$OUT/SHA256SUMS"

10편 Triage 스크립트의 run() 함수도 같은 원리(명령·시각·결과를 >로 묶어 저장)였다.

7-2. 공격자 관점에서 본 리다이렉션

공격 명령에는 흔히 > /dev/null 2>&1, nohup … &가 붙는다. 출력을 숨기고 세션이 끊겨도 계속 실행 되게 하기 위해서다. 그래서 관제에서는 명령의 출력이 아니라 실행 기록(auditd execve)과 프로세스 상태(34편 signal·백그라운드) 를 본다.

7-3. 분석 흐름

[분석 시작] set -o pipefail / noclobber, 케이스 디렉터리 생성
   ↓
[수집]     명령 | tee 케이스/NN_이름.txt  (오류도 함께 2>&1)
   ↓
[비교]     diff <(A) <(B) — SSH 출발지 vs 웹 출발지
   ↓
[검증]     PIPESTATUS·오류 파일로 누락 영역 확인
   ↓
[보존]     sha256sum → 보고서에 해시 기재

8. 핵심 정리

  • 모든 프로세스는 0(stdin)·1(stdout)·2(stderr) 로 시작한다.
  • |는 stdout만 다음 명령으로 넘긴다. 오류까지 넘기려면 2>&1 | 또는 |&.
  • 리다이렉션은 왼쪽부터 처리된다. 둘 다 파일로 보내려면 > file 2>&1 순서.
  • 파이프의 $?는 마지막 명령 것이다. 분석 스크립트에는 set -o pipefail.
  • tee, script로 분석 과정을 기록하고, noclobber로 덮어쓰기 사고를 막는다.
  • <(명령)으로 두 결과를 임시 파일 없이 비교할 수 있다.

9. 다음 글

다음 글 「28. Linux Shell Script 기초」 에서는 지금까지의 명령어 조합을 스크립트로 묶는다. 변수, 조건문, 반복문, 함수, 종료 코드를 정리하고 set -euo pipefail, 따옴표, 절대경로 같은 안전한 스크립트 작성 원칙 과 함께 인증 로그 요약 리포트 스크립트를 만든다.


참고 자료

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글