다잇소


[IT/트랜드] Shell Programming ⑪ – Shell에서 파일 기술자와 입출력 재지정 다루기

2016.08.07

보경생


 


Shell에서 파일 기술자와 입출력 재지정 다루기


우리는 세번째 강좌에서 표준 입출력에 관하여 얘기하였습니다. shell 다수의 파일 기술자를 사용할 수 있지만, 표준 입출력과 오류가 정해져 있고 파일 기술자는 3번에서 9번까지 사용이 가능합니다. 이러한 파일 기술자를 어떻게 사용할 수 있는지 정리 해 나가 보겠습니다.


1. 파일 기술자를 이용한 입출력 재지정


1. 표준출력과 표준오류 재지정


우리는 >, >>의 의미를 다시 한 번 상기 할 필요가 있습니다. >는 앞의결과를 파일에 저장한다는 것이고, >>는 추가한다는 의미 입니다.






















기호



사용법



의미



>



ls > filelist.txt



현재 디렉토리의 ls결과를 filelist.txt파일 만들어서 저장



>>



ls >> filelist.txt



현재 디렉토리의 ls결과를 filelist.txt파일 있는 경우는 내용을 추가하고, 없으면 만들어서 저장



우리는 재지정 기호를 이용하여 임시로 특정 내용을 특정 파일에 저장할 수 있습니다.










bkpark@[/data/dev/shell/bash_util/study/char11]$ cat exam01.sh


#!/bin/bash


echo Good results is expected > result.txt


ls -al >> result.txt


echo Bad results is expected >> result.txt


ls -al badfile 2>> result.txt


bkpark@[/data/dev/shell/bash_util/study/char11]$ ./exam01.sh


bkpark@[/data/dev/shell/bash_util/study/char11]$ cat result.txt


Good results is expected


total 36


drwxr-xr-x 2 bkpark bkpark 4096 86 12:05 .


drwxr-xr-x 10 bkpark bkpark 4096 84 21:24 ..


-rwxr-xr-x 1 bkpark bkpark 149 86 12:03 exam01.sh



-rw-r–r– 1 bkpark bkpark 25 86 12:05 result.txt


Bad results is expected


ls: cannot access ‘badfile’: No such file or directory



여기서 >>>를 이용하여 , 필요할 때마다 방향을 재지정하였습니다. 이렇게 임시로 매번 처리할 수 있지만, 매번 이렇게 처리 하는 것은 번거로울 수 밖에 없습니다. 그래서, 임시가 아닌 지속적으로 파일 기술자를 재지정 할 필요가 있습니다.


그래서 , 파일 기술자를 재지정할 명령어가 필요합니다. 파일기술자끼리는 shell 에서 파일 입출력을 재지정을 하려면 exec명령어를 사용해야 합니다.


형식 : exec #파일기술자 > filename or >&#file기술자










bkpark@[/data/dev/shell/bash_util/study/char11]$ cat exam02.sh


#!/bin/bash


# exec &> result2.txt로 줄여서도 쓸 수 있음


exec 1>result2.txt


exec 2>&1


echo Good results is expected


ls -al


echo Bad results is expected


ls -al badfile


bkpark@[/data/dev/shell/bash_util/study/char11]$ ./exam02.sh


bkpark@[/data/dev/shell/bash_util/study/char11]$ cat result2.txt


Good results is expected


total 36


drwxr-xr-x 2 bkpark bkpark 4096 86 12:15 .


drwxr-xr-x 10 bkpark bkpark 4096 84 21:24 ..


-rwxr-xr-x 1 bkpark bkpark 149 86 12:03 exam01.sh


-rwxr-xr-x 1 bkpark bkpark 122 86 12:15 exam02.sh



-rw-r–r– 1 bkpark bkpark 25 86 12:16 result2.txt


Bad results is expected


ls: cannot access ‘badfile’: No such file or directory



이번에는 표준 출력을 result2.txtexec를 재지정하여 주고, 다시 표준 오류 출력을 다시 표준 출력으로 재지정할 결과,


몇 번 표준 출력과 표준 오류 출력을 재지정할 필요 없이 우리가 원하는 결과 파일에 저장 할 수 있습니다.


2. 표준입력 재지정


표준 출력을 재지정 했듯이, 우리는 표준 입력을 다시 지정할 수 있습니다. 역시, exec 명령어 해결 할 수 있습니다. 우리는 표준입력을 read 읽을 수 있다는 것을 기억하고 있습니다. 사용자가 몇 명인지 알아 보겠습니다.










bkpark@[/data/dev/shell/bash_util/study/char11]$ cat ./exam03.sh


#!/bin/bash


count=0


# /etc/passwd를 표준입력으로 지정


exec 0</etc/passwd


while read line


do


((count++))


done


echo The number of users is $count.


bkpark@[/data/dev/shell/bash_util/study/char11]$ ./exam03.sh


The number of users is 44.



3. 기타 파일 기술자 사용


표준 입출력뿐만 아니라, 3~9까지 파일 기술자도 사용이 가능합니다 . 10개를 초과하는 경우는 다른 방법을 사용하여야 합니다. 다른 파일 기술자를 이용하여 출력 결과를 각각 다른 파일에 저장 해 보겠습니다.










bkpark@[/data/dev/shell/bash_util/study/char11]$ cat exam04.sh


#!/bin/bash


exec 3>std.out.txt


exec 4>std.err.txt


exec 1>&3


exec 2>&4


echo “The result will be correct”.


ls -al


echo “The result will be an error”.>&2


ls -al /etc/baddir


bkpark@[/data/dev/shell/bash_util/study/char11]$ ./exam04.sh


bkpark@[/data/dev/shell/bash_util/study/char11]$ tail std*


==> std.err.txt <==


The result will be an error.


ls: cannot access ‘/etc/baddir’: No such file or directory


==> std.out.txt <==


drwxr-xr-x 2 bkpark bkpark 4096 86 13:31 .


drwxr-xr-x 10 bkpark bkpark 4096 84 21:24 ..



-rwxr-xr-x 1 bkpark bkpark 172 86 13:31 exam04.sh


-rw-r–r– 1 bkpark bkpark 518 86 13:15 result2.txt


-rw-r–r– 1 bkpark bkpark 0 86 13:34 std.err.txt


-rw-r–r– 1 bkpark bkpark 28 86 13:34 std.out.txt



예상대로 표준 출력은 파일 기술자 3std.out.txt로 결과가 나왔고, 표준 오류는 파일 기술자 4std.err.txt로 나왔습니다. 파일 기술자는 script가 종료되는 순간 파일이 종료가 됩니다. 그렇지만, 우리는 파일을 중간에 닫아야 될 때도 있습니다. 파일 종류는 다음과 같이 하면 됩니다.


형식 : exec 파일기술자 >&- # 출력종료


exec 파일기술자<&- # 입력종료










bkpark@[~/bash_util/study/char11]$ cat exam05.sh


#!/bin/bash


exec 3> std.out.txt


echo “File descriptor 3 is open” >&3


# 파일기술자 3번을 닫는다.


exec 3>&-


echo “File descriptor 3 is closed” >&3


bkpark@[~/bash_util/study/char11]$ ./exam05.sh


./exam05.sh: line 5: 3: Bad file descriptor


bkpark@[~/bash_util/study/char11]$ cat std.out.txt


File descriptor 3 is open



4. 동시에 입출력 사용하기


입출력을 하나의 파일 기술자로 지정하는 것 가능합니다. 그렇지만, 이럴 경우 파일의 읽고 쓰는 위치를 제대로 인식하지 못하면 우리는 예상못하는 결과에 부디칠 수 있습니다.










bkpark@[~/bash_util/study/char11]$ cp std.out.txt2 std.out.txt


bkpark@[~/bash_util/study/char11]$ cat std.out.txt2


The first line will not be changed.


The second line will be changed.


The third line will not be changed.


bkpark@[~/bash_util/study/char11]$ cat exam07.sh


#!/bin/bash


#동시에 입출력용으로 파일을 연다.


exec 3<> std.out.txt


index=1


while read line


do


if [ $index -eq 2 ]


then


echo -n “The second is changed” >&3


fi


((index++))


done <&3


bkpark@[~/bash_util/study/char11]$ ./exam07.sh


bkpark@[~/bash_util/study/char11]$ cat std.out.txt


The first line will not be changed.


The second line will be changed.


The second is changedot be changed.



결과를 확인해 보면, 2번째 줄까지 읽었을 때 쓰기 시도를 합니다. 현재 파일 기술자 3은 위치는 3번째 라인을 가리키고 있어서, 그 지점에서 쓰기를 해 버립니다. 그래서, 입력과 출력을 동시에 지정할 때는 주의해서 해야 합니다.


4. 예제를 통한 입출력 재지정 풀이


1) 정상적인 결과는 버리고 오류만 메시지 처리 방법










bkpark@[~/bash_util/study/char11]$ cat exam08.sh


#!/bin/bash


echo The first normal message.


echo The first error message. >&2


echo The second normal message.


echo The second error message. >&2


bkpark@[~/bash_util/study/char11]$ ./exam08.sh 1>/dev/null


The first error message.


The second error message.



/dev/null를 알면 간단하게 오류만 화면에 나타내고, 정상적인 메시지를 버릴 수 있습니다. 표준 출력만 보고 싶을 때는 1>/dev/null 대신에 2>/dev/null를 사용하면 됩니다.


2) 표준 오류는 error.log로 별도로 저장하고, 표준출력과 표준오류는 stdout.log에 저장










bkpark@[~/bash_util/study/char11]$ ls


exam01.sh exam02.sh exam03.sh exam04.sh exam05.sh exam06.sh exam07.sh exam08.sh


bkpark@[~/bash_util/study/char11]$ ((exam08.sh 2>&1 1>&3 | tee error.log) 3>&1 ) > stdout.log


bkpark@[~/bash_util/study/char11]$ tail error.log stdout.log


==> error.log <==


The first error message.


The second error message.


==> stdout.log <==


The first normal message.


The second normal message.


The first error message.


The second error message.



이번에는 꽤나 복잡해 보입니다. 해석 순서는 exam08.sh 2>&1 1>&3 => | tee error.log) 3>&1 => > stdout.log로 해석 해 나가면 됩니다.


첫번째, 2>&1 1>&3은 표준오류는 표준출력가는 파이프가 만들어지고 이는 tee에서 표준입력으로 갑니다. 표준출력1번은 파일3번으로 바꾸어 지정이 되어 있습니다. 이 자체만으로 파일기술자 3번은 열려 있지 않습니다. 이것만 따로 실행하면 다음과 같은 결과를 볼 수 있습니다.










bkpark@[~/bash_util/study/char11]$ exam08.sh 2>&1 1>&3


bash: 3: Bad file descriptor



두번째, | tee error.log에서는 파이프에 도달한 표준오류의 출력 내용을 error.log에 저장을 하고, 다시 파일기술자 3번은 표준 출력을 지정되면 표준출력과 표준오류가 표준출력으로 나오게 됩니다.










bkpark@[~/bash_util/study/char11]$ (exam08.sh 2>&1 1>&3 | tee error.log) 3>&1


The first normal message.


The second normal message.


The first error message.


The second error message.


bkpark@[~/bash_util/study/char11]$ cat error.log


The first error message.


The second error message.



세번째, > stdout.log에서는 이제 간단하게 저장만 하면 해결이 됩니다.


2)번은 예를 이해하였으면, 파일 입출력 재지정에 대한 고민은 이제 없을 것 입니다.



2. 파일 기술자 찾아내기


너무 많은 파일 기술자가 사용이 되면 , 어떠한 파일 기술자가 사용이 되는지 혼란 스러울 수 있습니다. 이를 쉽게 찾아낼 수 있는 명령어 lsof가 있습니다. lsof라는 현재 시스템에서 있는 모든 파일 기술자를 찾아 주다 보니, 권한을 가지고 있는 사용자만 사용이 가능합니다. 일반적으로 /usr/sbin/이 있을 수도 있습니다. 그래서, 명령어가 어디에 있는지 확인 한 다음 실행하기 바랍니다.


lsof는 명령을 한 번 실행 해 보겠습니다.










bkpark@[~/bash_util/study/char11]$ lsof | tee listofds | wc -l


50302


bkpark@[~/bash_util/study/char11]$ head -5 listofds


COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME


systemd 1 root cwd unknown /proc/1/cwd (readlink: Permission denied)


systemd 1 root rtd unknown /proc/1/root (readlink: Permission denied)


systemd 1 root txt unknown /proc/1/exe (readlink: Permission denied)


systemd 1 root NOFD /proc/1/fd (opendir: Permission denied)



파일기술자가 50302가 있다는 것을 알 수 있습니다. 이러한 파일 기술자에는 장치, 소켓, 파이프등 다양한 것들이 있습니다. Unix는 모든 것이 파일로 통한 다고 생각해도 됩니다. 일단 우리가 관심 있는 표준출력, 표준입력, 그리고 표준오류를 찾아 보겠습니다.










bkpark@[/dev]$ lsof -a -p $$ -d 0,1,2


COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME


bash 4659 bkpark 0u CHR 136,0 0t0 3 /dev/pts/0


bash 4659 bkpark 1u CHR 136,0 0t0 3 /dev/pts/0


bash 4659 bkpark 2u CHR 136,0 0t0 3 /dev/pts/0



-a옵션은 다음의 결과가 AND조건으로 만들고, -p옵션은 프로세스 ID인데, $$현재 프로세스ID이니까, 현재 프로세스에서 찾는 것을 의미 합니다. -d옵션은 찾을려고 하는 파일 기술자 번호 입니다. 그래서 3가지 파일 기술자를 찾았습니다.


다음은 script내에서 파일 기술자를 열고 닫을 때 어떻게 lsof가 동작하는지 보겠습니다.










bkpark@[~/bash_util/study/char11]$ cat exam09.sh


#!/bin/bash


echo “FD 3, 4, and 5 will be created soon.”


#파일 기술자들을 연다


exec 3<>file1.txt 4>file1.txt 5<file1.txt


lsof -a -p $$ -d 3,4,5


echo “FD 3,4, and 5 will be closed soon.”


#파일 기술자들을 닫는다


exec 3>&- 4>&- 5<&-


lsof -a -p $$ -d 3,4,5


bkpark@[~/bash_util/study/char11]$ ./exam09.sh


FD 3, 4, and 5 will be created soon.


COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME


exam09.sh 7595 bkpark 3u REG 8,3 0 7471399 /data/dev/shell/bash_util/study/char11/file1.txt


exam09.sh 7595 bkpark 4w REG 8,3 0 7471399 /data/dev/shell/bash_util/study/char11/file1.txt


exam09.sh 7595 bkpark 5r REG 8,3 0 7471399 /data/dev/shell/bash_util/study/char11/file1.txt


FD 3,4, and 5 will be closed soon.



파일 기술자 3,45이 을 열었다 닫히는 것을 확인 할 수 있습니다. 그런데 파일 기술자 3다음 u, r, w를 확인 할 수 있습니다. 이는 r은 읽기 전용, w는 쓰기 전용, 그리고 u는 읽기와 쓰기가 모두가 가능하다는 얘기 입니다.


이상으로 파일 기술자와 입출력 재지정에 대한 얘기를 마치겠습니다.

설정된 프로필 사진이 없습니다.
| Wise리더
관심분야 IT,여행,맛집

카테고리 레이어 닫기