본문 바로가기

Tools and Utilities/Linux

[Linux 실습] Ch8.2. 심화 리다이렉션(Advanced Redirection)

오픈 소스 비영리 기관 'Open Source Initiative'의 로고. https://opensource.org/

본 글은 학교 '오픈소스 소프트웨어 입문' 과목을 수강하며 실습(또는 공부)한 내용을 정리한 글입니다.

개인 기록 용도로 작성한 글이라, 직접 이 글을 보고 실습을 따라하기엔 어려움이 많을 것으로 예상됩니다.
혹시 본 글을 보며 Linux를 공부하실 목적이라면, 일단 키워드 위주로 AI에게 물어봐가면서, 본인의 Linux 터미널에서 직접 타이핑해가며 공부하시기를 추천드립니다.

질문이나 의견이 있다면 자유롭게 댓글 달아주세요!

아까 그 덮어쓰기 > clobbering말고, >>는 append임.
쨌든, 내가 > 써서 파일 생기는 게 싫으면?

방법1. 
cd lab0325
rm out.txt
ls > ../out.txt # 여기서 out.txt가 바깥 디렉토리에 생김
cp ../out.txt .
cat out.txt

방법2.

! 시험문제 !

cd ..
rm out.txt
ls -a *
ls -a * > out.txt
echo * <- ls랑 같음.

cat은 파일 내용 보는거고, 변수나, 설정 보고싶으면 echo
*는 현재 디렉터리 내의 모든 파일과 폴더를 의미함.
즉, ls -a *는 각 폴더 안의 내용까지 출력함.

ls -a * | wc
ls -a * | wc - l
8
ls -a * > aa.result
wc -l aa.result
출력? -> 8 aa.result

ls -a > bb.result
ls
12개가 나옴.

중요) ls -a * > aa.result
여기서 *는 현재 디렉터리의 모든 파일과 폴더를 
의미하지만, 하위 디렉터리까지 확장됨.
ls -a *가 실행되면, 쉘이 먼저 *를 
확장(globbing)해서 ls -a에 전달함.
또한, *를 쓰면 -a 해도 숨김 파일 자체는 포함 안 됨.
그냥 글로빙을 먼저 한 다음에, 그 글로빙 결과 파일/디렉토리에
대해, 개별적으로 ls -a를 실행하는 거임.


<오늘실습시작>
mkdir lab0327
cd lab0327
touch a b c
ls a b c d 결과는? -> 이제 쓸 수 있어야 함.
2> err.txt ls a b c d 1>out.txt
! 시험문제 !
    cat err.txt 의 결과를 쓰시오
-> ls: cannot access 'd': No such file or directory
그냥 시험엔 d라는 파일이 없다고 에러가 나옵니다.
하고 써도 맞게는 해주겠다고 함.
2. cat out.txt의 결과를 쓰시오.
-> a
b
c
이거 근데 한 줄로 써도 맞게는 해줄거라함.

예전에도 말했지만, 여담) 이거 ls 쓰면 한 줄로 되는데,
ls -l 쓰면 new line이 있는 이유??
-> 원래 new line이 있는 게 맞는데 실제론, -l 옵션이 없으면 
ls를 썼을 때 stdout이면 new line이 없게 한 줄로 출력해주는 거.

<한 파일에 다 몰아넣는 방법??>
방법1. -> 좀 이상함.
ls a b c > all.txt
ls d 2>> all.txt
cat all.txt
a
b
c
ls: cannot access 'd': No such file or directory


방법2. 권장되는 방법.
ls a &> all.txt
ls a b c d &> all.txt 
ls a b c d >& all.txt
-> stdout과 stderr을 동시에 리다이렉션(위 두개 같음)


! 시험무조건!!!!!!!!!!!!! ! !시험출제!
&> 1>&2 2>&1
이거 ㅈㄴ 어려움.

ls a b c d 2>&1
-> stderr을 stdout로 리다이렉션
-> 둘다 stdout로 내보냄.

&> 
1>&2
2>&1

ls a b c d 1>std.out 2>&1
cat std.out
ls: cannot access 'd': No such file or directory
a
b
c

저거 stderr stdout 순서는 랜덤이라는듯?
stdout과 stderr은 별도의 버퍼(buffer)를 사용하며,
실행 환경에 따라 출력되는 순서가 달라질 수 있음
이거 메모리 I/O에 비해 input장치 output장치는 겁나 느림.
그래서 일단 메모리에 버퍼에다가 입출력을 받는데,

여담) 버퍼가 뭐냐? 
10점 만점에 5점이라도 받으려면, "메모리의 공간입니다"로 끝나면 됨.
아웃풋 버퍼: 아웃풋 버퍼로 쓰기 위한 메모리 공간입니다 -> 9점
아웃풋 버퍼: 아웃풋 버퍼가 일반적으로 CPU나 메모리 I/O보다 훨씬 느리기 때문에, ~~~ -> 10점


터미널이 생기면 stdout, stdin, stderr이 생기면서
입출력 버퍼가 만들어짐.
이 때, 총 3개의 버퍼가 만들어짐. 각각의 버퍼.
에러 메시지는 stderr로, 일반적인 출력은 stdout로.
근데, 이 메모리 버퍼에 쓰여져 있는 걸, 누구 먼저 내보낼까? 하는 건
리눅스 시스템이 그냥 아무거나 내보내 줌. 랜덤임.
이거 컨트롤할 수 있나요? -> 있음. flush 명령어 쓰면 됨.
출력 버퍼를 변기라고 생각해도 됨. flush로 물 내리는거.


printf는 c 함수라 man 3에 있는데,
실제 man printf 해보니 1에 있음!!
-> 그러면 리눅스에도 printf command가 있단 거임.

1	실행 가능한 명령어 (사용자 명령어)	ls, cp, grep, vim
2	시스템 콜 (커널이 제공하는 함수)	open(), read(), write(), fork()
3	라이브러리 함수 (C 표준 라이브러리)	printf(), malloc(), strlen()

printf "%d %d %f\n" 20 30 25.4
20 30 25.400000
참고로, 20 30 25.4는 숫자가 들어간 게 아님. 문자열임.
특히 리눅스 쉘에서 플로팅 포인트는 존재하지 x.



! 시험문제 !
ls a b c d 1>std.out 2>&1
-> stderr을 stdout이 가리키는 곳으로 리다이렉션하라는 뜻이기에,
-> stderr도 std.out 파일로 리다이렉션됨.
ls a b c d 2>&1
각각 파일, 화면에 다 뿌림

ls a b c d 2>&1 1>std.out
그럼 이건?
ls: cannot access 'd': No such file or directory
가 stdout로, 나머진 std.out로.

tar cvf > aa <- 이거 에러임. f 다음엔 파일네임.
근데, 그러니까 >로 시작하는 파일명 만들 수 없어보이지 않음?

echo aa > ">aa"
-> >aa라는 파일을 만들고 거기에 aa를 넣음
ls -al

echo aa > ">aa"
ls -al
total 28
drwxr-xr-x  2 foss110 foss 4096 Mar 27 09:39  .
drwx------ 15 foss110 foss 4096 Mar 27 09:30  ..
-rw-r--r--  1 foss110 foss    0 Mar 27 09:14  a
-rw-r--r--  1 foss110 foss    3 Mar 27 09:39 '>aa'
-rw-r--r--  1 foss110 foss   55 Mar 27 09:20  all.txt
-rw-r--r--  1 foss110 foss    0 Mar 27 09:14  b
-rw-r--r--  1 foss110 foss    0 Mar 27 09:14  c
-rw-r--r--  1 foss110 foss   49 Mar 27 09:14  err.txt
-rw-r--r--  1 foss110 foss    6 Mar 27 09:14  out.txt
-rw-r--r--  1 foss110 foss   55 Mar 27 09:36  std.out

보면, 만들어짐!! >aa
근데... 저렇게 하면 안됨.
왜냐면 저러면 >때문에 커맨드 칠 때 막 오류남.
그래서 ' '를 써서 토큰을 묶어주면 되는데,
묶어서 지워버리자. rm '>aa'



! 시험문제 ! 중간고사 모드 복귀

ls a b c d 1>std.out 2>&1
ls a b c d 2>&1 1>std.out
ls: cannot access 'd': No such file or directory

위에 껀 stderr을 터미널로 출력 x, 밑에는 출력 o
위에는 stdout로 나갈 걸 std.out로 보내고,
stderr을 stdout로 리다이렉션한단건데,
아래는 stderr을 먼저 stdout로 리다이렉션하고,
그 다음에 stdout로 나갈 걸 std.out로 보낸다는 거임.
그래서 이 순서때문에 아래껀 stderr가 stdout로 나감.


리눅스는 디바이스도 다 파일이라 했잖음?
pushd /dev
ls
-> 보면 중간에 stderr, stdin, stdout가 파란색으로 나옴.
ls -al std*
lrwxrwxrwx 1 root root 15 Feb 25 18:57 stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Feb 25 18:57 stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Feb 25 18:57 stdout -> /proc/self/fd/1
저 오른쪽에 화살표가 나와있다는건,
저 세 파일이 '소프트 링크'라는 거임. not 하드 링크.

저 0 1 2를 만나는 순간 너무 기쁨 ㅎㅎ
우리가 공부한 0 1 2랑 동일!!
저 fd라고 하는 거는 파일 디스크립터.

직접 찾아가보자!!
cd /proc/self/fd/
ls -al
total 0
dr-x------ 2 foss110 foss  0 Mar 27 09:45 .
dr-xr-xr-x 9 foss110 foss  0 Mar 27 09:44 ..
lrwx------ 1 foss110 foss 64 Mar 27 09:45 0 -> /dev/pts/74
lrwx------ 1 foss110 foss 64 Mar 27 09:45 1 -> /dev/pts/74
lrwx------ 1 foss110 foss 64 Mar 27 09:45 2 -> /dev/pts/74
lrwx------ 1 foss110 foss 64 Mar 27 09:45 255 -> /dev/pts/74

(복습) 이거 왜 다 같은 pts/74를 가리키지? st family가? 확인

이거 교수님꺼랑 다른 pts/숫자를 가리켜요!
-> 이거 원래 그럼. 우리 서버가 접속 시마다 다른 pts를 할당해줌.
즉, st family의 심볼릭 링크를 따라가니,
그게 다른 곳으로 다시 심볼릭 링크가 되어 있는데,

tty
/dev/pts/74
저 /dev/pts/74는 터미널임!! 가상 터미널 배웠잖아.


popd
ls a b c d 2>&1 1>std.out 설명)

(틀림)stderr이 dev/pts/74으로 바뀌었음 -> 아님.
윗 줄의 설명은 틀림!! 
이거 위에 보다시피, 이미 stdout stdin 둘 다 74라는 가상 터미널을 가리키는 소프트 링크임! 
stdout처럼 작동하게끔 바뀌었다고 이해하면 편함. 

그리고 stdout를 std.out으로 바꾸었음.
그래서 stdout만 파일로 나가고, stderr은 stdout로 나간 거임.

결론) 리다이렉션이 여러 개 있을 때 순서는 매우 중요하다.

여담1) 리눅스 /bin과 /usr/bin의 차이(교수님 답변)
리눅스에서 안전 모드로 부팅되면, 싱글 유저 모드로 열리는데,
그 싱글 유저가 루트고 그 루트만을 위한 터미널이 열림.
그게 tty0 터미널이고, 이걸 콘솔이라고 부름.
근데 이 싱글 모드에서 열린 콘솔에선 /bin에 있는 것밖엔 사용을 못함.
그리고 평소에 우리가 일반적인 멀티 유저에서 실행했을 때는 /usr/bin에 있는 걸 사용함.
그래서 양쪽에서 다 접근 가능해야 하는데,
원칙상은 이렇고 요즘 대부분 리눅스 배포판에선 /bin -> /usr/bin으로 심볼릭 링크를 설정해 놓음.
(실제로 테스트해보니 그랬음)

여담2) 터미널에 std family 소프트 링크가 연결되는 원리(교수님 답변)
우리가 ssh로 접속하면, 이 서버에서 너 아이디 있어? 있네. 비번 일치해? 일치하네. 하는 거를
/etc/passwd에 있는 거랑 대조해서, 통과하면 가상 터미널을 열어줌(tty46 이런 거, 파일로는 pts/46)
그러고 나서 login 프로세스가 끝나는데(실행 타이밍은 애매)
얘가 끝나기 전에 우리 pts46 파일에 std family를 생성시켜줌.
그래서 얘네가 심볼릭 링크로 저걸 가리키고 있던 거.

그리고 세 std가 모두 같은 pts46이라는 장치 파일을 심볼릭 링크로 가리키고 있지만,
저 세 개가 다르게 작동하는게 그거는 지금 수준에선 투머치 정보.
아마 파일 내용에서 뭔가 달라지는 거 같은데 암튼 다르게 구분됨.




vi a 
(아무거나 막)
cat a

vi a
cat a
1weqeadasasd
asdasdsd
adasdad
adadadda

여담) cat a 하면 뭐가 일어나냐?
-> a 파일의 내용을 stdout으로 출력해 준다. (6점)

cat a > b
이 명령의 의미는 뭘 것같음? 의도는 뭘 것같음?
어떤 일이 일어나겠냐?
파일 a의 내용을 b 파일로 복사합니다(복사라는 말이 들어가면 맞았다고 함)
(근데, 덮어쓰기가 더 맞지 않나..)

ls < a > c 
그럼 이거는??
똑같음. a 파일의 내용을 c로 복사(덮어쓰기가 맞지 않나..)합니다.

man cat 해보니,
옵션들 붙을 수 있고, 파일 이름은 생략 가능. 
파일이름 생략 시 stdin으로 직접 입력받음.

그러면, cat < a 는 파일이 생략된 거임!
그래서 stdin으로 받으려 하는데, 거기에 a가 들어간 거.

그럼, cat < a b 하면??
cat b < a 하면??

cat b < a 하면
b의 내용이 출력됨.
cat < a b 하면 
b의 내용이 출력됨.
즉, 파일이름 생략 시 stdin으로 입력받는 그게 여기선 작동하지 않음.
파일이름이 존재하니까! stdin으로 a가 들어오든 말든 무시해버림.
즉, stdin이 작동하게 하려면, 파일 이름이 없어야 함.
지금은 저 cat < a b는 b라는 파일이 존재하는 거임.
리다이렉션은 위치가 상관없다 했으니까.

왜냐면, 파일 이름이 없거나, 파일 이름이 -이면,
stdin이 작동한다고 man에 써져있기 때문.

! 시험문제 ! 
echo AAAA > a
cat a
AAAA
cat a a a > aaa
cat aaa 
결과는? 
AAAA
AAAA
AAAA

cat aaa aaa > a8
cat a8
AAAA
AAAA
AAAA
AAAA
AAAA
AAAA

echo BBBB >> aaa
cat aaa
AAAA
AAAA
AAAA
BBBB


ls -al > result
이거와 같은 기능을 복잡하게 써보면,
ls -al | cat > result
위와 같은 결과가 나옴.
ls -al의 stdout이 cat > result의 stdin로 들어감.

근데, ls -al | cat하면
ls -al와 같은 결과가 튀어나옴. 
왜냐면 cat은 파일이름 생략 시 stdin으로 입력받고,
그걸 그대로 stdout에 출력해 주니까.

ls -al . ddd | cat > result
하면 ddd가 없다는 에러 메시지가 stderr로 출력되고,
result에는 에러 메시지는 안 써지고 ls -al . 결과만 써짐. 

ls -al . ddd |& cat > result
이렇게 해야 에러 메시지까지 result에 써짐.
왜냐면 |는 stdout만 stdin으로 보내는 거니까.
저거 &는 |에 딱 붙여 써야함.
참고로 |&은 안됨.


---

mkfifo mypipe
ls -al
total 44
drwxr-xr-x  2 foss110 foss 4096 Mar 27 10:02 .
drwx------ 15 foss110 foss 4096 Mar 27 09:50 ..
-rw-r--r--  1 foss110 foss    5 Mar 27 09:57 a
-rw-r--r--  1 foss110 foss    9 Mar 27 09:40 aa
-rw-r--r--  1 foss110 foss   15 Mar 27 09:57 aaa
-rw-r--r--  1 foss110 foss   55 Mar 27 09:20 all.txt
-rw-r--r--  1 foss110 foss   39 Mar 27 09:53 b
-rw-r--r--  1 foss110 foss    0 Mar 27 09:14 c
-rw-r--r--  1 foss110 foss   49 Mar 27 09:14 err.txt
prw-r--r--  1 foss110 foss    0 Mar 27 10:02 mypipe
-rw-r--r--  1 foss110 foss    6 Mar 27 09:14 out.txt
-rw-r--r--  1 foss110 foss   52 Mar 27 10:02 result
-rw-r--r--  1 foss110 foss    6 Mar 27 09:41 std.out

보면, 중간에 mypipe를 보면,
맨 앞에 파일 타입이 p임.
-> 이건 이름 있는 파이프라는 뜻임. 네임드 파이프.
일반적인 파이프(|)와 달리 파일 시스템에 존재하는 파이프라는

cat mypipe & 
&는 여기선 작업을 백그라운드로 돌린다는 거임.

foss110@ajousw:~/lab0327$ cat mypipe &
[1] 1842746
foss110@ajousw:~/lab0327$ echo YAYAYAYAYA > mypipe
YAYAYAYAYA

저 YAYAYAYAYA는 cat 프로세스가 출력한 거임.
mypipe라는 곳에 echo로 넣어줘서, 그걸
cat 프로세스가 출력한 것.
cat 프로세스를 백그라운드 작업으로 돌린 거임.

이런 백그라운드 작업 돌릴 때 네임드 파이프 쓰면 됨.

echo "Hello, World!" > mypipe  # 데이터를 파이프에 입력
cat < mypipe  # 파이프에서 데이터를 읽음
Hello, World!
이런 것도 됨. IPC에 사용됨.