12.5. 파일, 아카이브(archive) 명령어

아카이빙

tar

유닉스의 표준 아카이브(archive) 유틸리티. 원래는 Tape ARchiving 프로그램에서 왔는데, 이 프로그램은 테입 드라이브부터 보통 파일, 심지어는 표준출력(예 4-3 참고)까지 포함하는 모든 종류의 디바이스에 대해서 모든 종류의 아카이브를 다룰 수 있도록 만들어 졌습니다. GNU tar는 오래전부터 gzip 압축을 다룰 수 있는 옵션이 패치되어 있었는데, tar czvf archive-name.tar.gz * 라고 하면 하위 디렉토리를 포함한 모든 파일을 묶어서 압축하라는 뜻입니다(도트파일은 제외).

유용한 tar 옵션 몇 가지:

  1. -c 만들기(새 아카이브)

  2. --delete 지우기(아카이브에 들어 있는 파일)

  3. -r 덧붙이기(파일을 아카이브로)

  4. -t 목록(아카이브 내용)

  5. -u 아카이브 업데이트

  6. -x 뽑아내기(아카이브에 들어 있는 파일)

  7. -z 아카이브를 gzip 으로 압축

경고

gzip으로 묶인채 손상된 tar 아카이브는 복구하기가 매우 힘들기 때문에 중요한 파일을 아카이브로 만들때는 여러 군데에 백업을 해 놓기 바랍니다.

shar

쉘 아카이브 유틸리티. 쉘 아카이브 파일은 실제로는 #!/bin/sh 헤더와 아카이브를 풀기 위한 명령어들로 이루어진 쉘 스크립트로써, 압축되지 않은 파일들이 쭉 붙어 있는 파일입니다. shar 아카이브는 아직도 인터넷 뉴스 그룹에서 볼 수 있는데 여기 말고 다른 곳에서는 tar/gzip 때문에 거의 안 씁니다. shar 아카이브는 unshar 명령어로 풀어 줍니다.

ar

주로 바이너리 오브젝트 파일 라이브러리에서 쓰이는 아카이브를 위한 생성, 조작 유틸리티.

cpio

이 특화된 아카이브 복사 명령어(copy input and output)는 tar/gzip 때문에 이제 거의 안 쓰이지만 디렉토리 트리를 옮기려는 경우등의 쓰임새가 아직 남아 있습니다.

예 12-22. cpio로 디렉토리 트리 옮기기

#!/bin/bash

# cpio 로 디렉토리 트리를 복사하기.

ARGS=2
E_BADARGS=65

if [ $# -ne "$ARGS" ]
then
  echo "사용법: `basename $0` source destination"
  exit $E_BADARGS
fi  

source=$1
destination=$2

find "$source" -depth | cpio -admvp "$destination"
# 여기서 쓰인 cpio 옵션이 무슨 뜻인지 알려면 맨 페이지를 읽어 보세요.

exit 0

예 12-23. rpm 아카이브 풀기

#!/bin/bash
# de-rpm.sh: 'rpm' 아카이브 풀기

E_NO_ARGS=65
TEMPFILE=$$.cpio                         # "유일한" 임시 파일.
                                         # $$ 는 스크립트의 프로세스 ID.

if [ -z "$1" ] 
then
  echo "사용법: `basename $0` filename"
exit $E_NO_ARGS
fi


rpm2cpio < $1 > $TEMPFILE                # rpm 아카이브를 cpio 아카이브로 변환.
cpio --make-directories -F $TEMPFILE -i  # cpio 아카이브 풀기.
rm -f $TEMPFILE                          # cpio 아카이브 지우기.

exit 0

압축

gzip

표준 GNU/UNIX 압축 유틸리티로서, 성능이 떨어지고 특허가 걸려 있는 compress를 대신합니다. 압축 풀기 명령어는 gunzip으로써, gzip -d와 같습니다.

zcat 필터는 gzip으로 묶인 파일의 압축을 풀어 표준출력으로 내보내기 때문에 파이프의 입력이나 재지향에서 쓰일 수 있습니다. 즉, 실제로는 입축 파일에 대해서 동작하는 cat이라고 보면 됩니다(옛날 compress 로 묶인 파일도 포함). zcatgzip -dc와 같습니다.

경고

몇몇 상업용 유닉스 시스템에서는 zcatuncompress -c와 동의어로 쓰이지만 gzip으로 묶인 파일에 대해서는 동작하지 않습니다.

예 7-6 참고.

bzip2

또 다른 압축 유틸리티로써, 특별히 크기가 큰 파일에 대해서는 gzip보다 더 효율적입니다. bzip2에 대한 압축 풀기 명령어는 bunzip2입니다.

compress, uncompress

상용 유닉스 배포판에서 찾을 수 있는 오래되고 특허가 걸려있는 유틸리티이고, 더 효율적인 gzip으로 거의 다 바뀌었습니다. gunzipcompress로 묶인 파일들을 풀 수 있지만, 리눅스 배포판들은 호환성을 위해서 compress를 닮은 명령어를 포함시킵니다.

작은 정보: znew 명령어는 compress로 압축된 파일을 gzip으로 압축된 파일로 변환해 줍니다.

sq

또 다른 압축 유틸리티로써 오직 정렬된 아스키 낱말 목록에 대해서만 동작하는 필터입니다. sq < input-file > output-file 처럼 표준 필터를 쓰듯이 쓰면 됩니다. 속도는 빠르지만 gzip만큼 효과적이지는 않습니다. 이 명령어에 해당하는 압축 풀기 필터는 unsq이고 사용법은 sq와 같습니다.

작은 정보: sq의 출력을 gzip에 파이프로 걸어서 더 압축 시킬 수도 있습니다.

zip, unzip

도스의 PKZIP과 호환되는 크로스 플랫폼 파일 아카이빙및 압축 유틸리티. 인터넷에서 "Zip"으로 묶인 아카이브들이 "타르볼"보다 더 많이 쓰입니다.

파일 정보

file

파일 종류를 구분지어 주는 유틸리티. file file-name 이라고 치면 ascii textdata같은 file-name에 대한 스펙을 알려줍니다. 이 명령어는 Linux/UNIX 배포판에 따라 /usr/share/magic이나 /etc/magic, /usr/lib/magic등에서 매직 넘버를 참고해서 파일 스펙을 알려줍니다.

-f 옵션을 쓰면 파일이름의 목록이 들어 있는 지정된 파일을 분석하면서 배치 모드로 동작합니다. -z 옵션은 대상 파일이 압축된 파일일 경우 강제로 압축이 풀린 상태의 파일 타입을 분석해 줍니다.

bash$ file test.tar.gz
test.tar.gz: gzip compressed data, deflated, last modified: Sun Sep 16 13:34:51 2001, os: Unix

bash file -z test.tar.gz
test.tar.gz: GNU tar archive (gzip compressed data, deflated, last modified: Sun Sep 16 13:34:51 2001, os: Unix)
	      

예 12-24. C 소스에서 주석을 제거하기

#!/bin/bash
# strip-comment.sh: C 소스에서 주석(/* 주석 */)을 제거해 줍니다.

E_NOARGS=65
E_ARGERROR=66
E_WRONG_FILE_TYPE=67

if [ $# -eq "$E_NOARGS" ]
then
  echo "사용법: `basename $0` C-program-file" >&2 # 에러 메세지는 표준출력으로.
  exit $E_ARGERROR
fi  

# 파일 타입이 맞는지 확인.
type=`eval file $1 | awk '{ print $2, $3, $4, $5 }'`
# "file $1" 이 파일 타입을 에코해 준 다음...
# awk 가 첫 번째 필드인 파일이름을 지워주고...
# 그 결과가 "type" 변수로 들어갑니다.
correct_type="ASCII C program text"

if [ "$type" != "$correct_type" ]
then
  echo
  echo "이 스크립트는 오직 C 소스 파일에 대해서만 동작합니다."
  echo
  exit $E_WRONG_FILE_TYPE
fi  


# 약간은 신비스러워 보이는 sed 스크립트:
#--------
sed '
/^\/\*/d
/.*\/\*/d
' $1
#--------
# sed 의 기본에 대해서 몇 시간만 투자를 하면 이해하기 쉽습니다.


# 주석이 코드와 같은 줄에 있는 경우를 처리하기 위해서는 
# 추가적인 sed 스크립트가 필요합니다.
# 약간은 어려울 수도 있지만 독자들을 위해서 연습문제로 남겨 놓습니다.

# 또, 위 코드는 우리가 바라지 않던 결과로서
# "*/" 이나 "/*" 인 줄도 지워버립니다.

exit 0


# --------------------------------------------------------------------
# 다음의 코드들은 위에서 'exit 0'이라고 했기 때문에 실행되지 않습니다.

# Stephane Chazelas 가 제안한 다른 방법:

usage() {
  echo "사용법: `basename $0` C-program-file" >&2
  exit 1
}

WEIRD=`echo -n -e '\377'`   # 혹은 WEIRD=$'\377'
[[ $# -eq 1 ]] || usage
case `file "$1"` in
  *"C program text"*) sed -e "s%/\*%${WEIRD}%g;s%\*/%${WEIRD}%g" "$1" \
     | tr '\377\n' '\n\377' \
     | sed -ne 'p;n' \
     | tr -d '\n' | tr '\377' '\n';;
  *) usage;;
esac

# 이것 역시 다음과 같은 경우에는 오동작을 합니다:
# printf("/*");
# 나
# /*  /* 주석에 주석이 들어감 */
#
# 특별한 경우들(문자열에 들어 있는 주석, 
# \"나 \\" 를 포함한 문자열에 들어 있는 주석...)을 모두 처리할 수 있는
# 유일한 방법은 C 파서(아마도 lex 나 yacc?)를 작성하는 것입니다.

exit 0
which

which command-xxx 라고 하면 "command-xxx"의 전체 경로명을 알려 줍니다. 시스템에 특정 명령어나 유틸리티가 설치되어 있는지 알아내려고 할 때 유용합니다.

$bash which rm
/usr/bin/rm

whereis

위의 which와 비슷하지만 "command-xxx" 맨 페이지의 전체 경로명도 같이 알려줍니다.

$bash whereis rm
rm: /bin/rm /usr/share/man/man1/rm.1.bz2

whatis

whatis filexxxwhatis 데이타베이스에서 "filexxx"를 찾아줍니다. 시스템 명령어와 중요한 설정 파일을 확인하고 싶을 때 유용합니다. 간단한 man이라고 생각하면 됩니다.

$bash whatis whatis
whatis               (1)  - search the whatis database for complete words

예 12-25. /usr/X11R6/bin 둘러보기

#!/bin/bash

# 도대체 /usr/X11R6/bin 에 들어있는 이상야릇한 실행파일들의 정체가 뭘까요?

DIRECTORY="/usr/X11R6/bin"
# "/bin", "/usr/bin", "/usr/local/bin" 같은 디렉토리에 대해서도 해보세요.

for file in $DIRECTORY/*
do
  whatis `basename $file`   # 실행파일에 대한 정보를 에코.
done

exit 0
# 이 스크립트의 결과를 재지향 하고 싶다면,
# ./what.sh >>whatis.db
# 혹은 표준출력에서 한 번에 한 쪽씩 보려면,
# ./what.sh | less

예 10-3 참고.

vdir

자세한 디렉토리 목록을 보여줍니다. ls -l 이라고 하는 것과 비슷합니다.

GNU fileutils 에 속하는 명령어입니다.

bash$ vdir
total 10
-rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.xrolo
-rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.xrolo.bak
-rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.xrolo

bash ls -l
total 10
-rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.xrolo
-rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.xrolo.bak
-rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.xrolo
	      

shred

파일을 지우기 전에 랜덤 비트 패턴을 여러번 덮어 써서 보안상 안전하게 지워줍니다. 예 12-33 과 동일한 결과를 가져오지만 더 빈틈없고 멋진 방법입니다.

GNU fileutils 에 속하는 명령어입니다.

경고

shred를 써서 파일을 지운다고 해도 최첨단 복구 기술(advanced forensic technology)을 이용해 그 내용의 일부나 전체를 복구해 내는 것을 막지는 못합니다.

locate, slocate

locate는 자체 데이타베이스에서 파일을 찾아 줍니다. slocatelocate(slocate로 별칭이 지정 되어 있을)의 보안 강화 버전입니다.

$bash locate hickson
/usr/lib/xephem/catalogs/hickson.edb

strings

strings를 쓰면 바이너리나 데이타 파일안에서 사람이 읽을 수 있는(출력 가능한) 문자를 찾을 수 있습니다. 해당 파일에서 출력 가능한 문자들을 순서대로 보여줍니다. 이 명령어를 쓰면 코어 덤프 파일을 간단하게 점검해 볼 수 있고 알 수 없는 그래픽 파일의 타입을 확인해 볼 수 있습니다(strings image-file | more 이라고 쳐서 JFIF같은 문자를 보여준다면 그 파일은 jpeg 파일이라고 보면 됩니다). 스크립트에서는 strings의 출력을 grep이나 sed로 파싱해서 쓸 수 있을 것입니다. 예 10-7예 10-8을 참고하세요.

유틸리티

basename

파일명에서 경로 정보를 떼어내고 오직 파일 이름만 보여 줍니다. basename $0 이라고 하면 스크립트는 자기가 쉘에서 불린 자기 이름을 알 수 있습니다. 스크립트에 필요한 인자가 없이 실행되는 경우에 "사용법" 메세지를 찍을 때 쓸 수 있습니다:
echo "사용법: `basename $0` arg1 arg2 ... argn"

dirname

파일명에서 basename을 떼어내고 오직 경로 정보만 보여줍니다.

참고: basenamedirname은 어떤 문자열에 대해서도 동작합니다. 이 명령어들에 넘겨줄 인자는 꼭 실제로 존재하는 파일이 아니여도 됩니다(예 A-6 참고).

예 12-26. basenamedirname

#!/bin/bash

a=/home/bozo/daily-journal.txt

echo "/home/bozo/daily-journal.txt 의 basename = `basename $a`"
echo "/home/bozo/daily-journal.txt 의 dirname = `dirname $a`"
echo
echo "내 홈 디렉토리는 `basename ~/`."               # 그냥 ~ 도 됩니다.
echo "내 홈 디렉토리의 홈은 `dirname ~/`."           # 그냥 ~ 도 됩니다.

exit 0
split

한 파일을 작은 조각으로 나눠주는 유틸리티로서 플로피에 백업을 하려고 하거나, 이메일의 첨부 파일로 쓰려고 할 때, 업로드를 하려고 할 때 주로 쓰입니다.

sum, cksum, md5sum

체크썸(checksum)을 생성해 주는 유틸리티입니다. 체크썸이란 파일의 실제 내용에 대해 산술적인 계산을 해 특정한 숫자를 뽑아낸 것입니다. 이를 이용해 파일의 무결성을 확인할 수 있습니다. 보안에 관련된 목적으로 아주 중요한 시스템 파일 내용이 변경되거나 손상됐는지 여부등을 체크썸 목록으로 관리하고 이를 참조하는 스크립트를 만들어 쓸 수도 있습니다. md5sum은 이런 보안 어플리케이션에 제일 적합한 명령어입니다.

인코딩과 암호화

uuencode

바이너리 파일을 아스키 문자로 인코드해서 이메일을 보내거나 뉴스 그룹에 포스팅 할 때 제대로 전송될 수 있게 해줍니다.

uudecode

uuencode된 파일을 디코드해서 원래 바이너리 파일로 만들어 줍니다.

예 12-27. 인코드된 파일을 uudecode하기

#!/bin/bash

lines=35        # 헤더용으로 여유있게 35 줄을 잡습니다.

for File in *   # 현재 디렉토리의 모든 파일을 확인...
do
  search1=`head -$lines $File | grep begin | wc -w`
  search2=`tail -$lines $File | grep end | wc -w`
  #  uuencode 된 파일은 앞 부분에 "begin"이란 말이 들어 있고,
  #+ 끝 부분에 "end"란 말이 들어 있습니다.
  if [ "$search1" -gt 0 ]
  then
    if [ "$search2" -gt 0 ]
    then
      echo "uudecode 중 - $File -"
      uudecode $File
    fi  
  fi
done  

#  이 스크립트의 인자로 이 스크립트 자체를 주게 되면 오동작을 할 텐데, 
#+ 왜냐하면 이 스크립트는 "begin"과 "end"를 모두 포함하고 있기 때문입니다.

# 연습문제:
# 뉴스 그룹 헤더를 확인하도록 수정해 보세요.

exit 0

작은 정보: 유즈넷 뉴스 그룹에서 받은 아주 긴 메세지를 uudecode할 때 fold -s 명령어를 파이프등에 걸어서 쓰면 아주 유용합니다.

crypt

예전에는 이 명령어가 유닉스의 파일 암호화 표준 유틸리티였습니다. [1] 정치적인 이유때문에 암호화 소프트웨어의 수출을 금지하는 정부의 규제로 인해 많은 유닉스 세계에서 crypt가 사라지게 됐고 아직도 거의 대부분의 리눅스 배포판에서 빠져 있습니다. 다행히도 많은 프로그래머들이 crypt를 적절히 대신할 프로그램을 많이 만들어 놨고 이들중에는 저자가 직접 만든 cruft 도 있습니다(예 A-4 참고).

잡동사니 명령어(Miscellaneous)

make

바이너리 패키지를 빌드및 컴파일해주는 유틸리티. 소스 파일에서 추가 변경 사항이 발생하면 정해진 동작을 하도록 하는데 쓰입니다.

make 명령어는 파일 의존성과 수행할 동작이 들어 있는 Makefile을 바탕으로 동작합니다.

install

특별한 목적을 갖고 있는 파일 복사 명령어로서 cp와 비슷하지만 복사될 파일의 소유권과 속성을 설정해 줄 수 있습니다. 이 명령어는 소프트웨어 패키지를 설치할 때에 딱 맞는 명령어처럼 보이는데, 실제로 Makefiles(make install : 섹션)에서 자주 등장합니다. 또한 설치 스크립트에서 찾아 볼 수도 있습니다.

more, less

텍스트 파일이나 스트림을 표준출력으로 한 번에 한 쪽씩 표시해 주는 페이저입니다. 스크립트의 출력을 위한 필터로 쓸 수 있습니다.

주석

[1]

이 명령어는 단일 시스템이나 지역 네트워크에 있는 파일을 암호화 하는데 쓰이는 대칭형 블럭 암호화(symmetric block cipher)명령어입니다. 이와 반대 개념인 "공개키" 암호화란 것도 있는 데 이 암호화의 유명한 예가 바로 pgp입니다.