12.9. 기타 명령어

명령어 목록

jot, seq

이 유틸리티들은 사용자가 정한 간격대로 정수값을 차례차례 만들어 냅니다. for 루프에서 아주 유용하게 쓰일 수 있습니다.

예 12-31. seq로 루프에 인자를 만들어 넣기

#!/bin/bash

for a in `seq 80`  # 혹은      for a in $( seq 80 )
# for a in 1 2 3 4 5 ... 80   과 똑같습니다(과도한 타이핑을 안 해도 되죠!).
# 시스템에 'jot'이 있다면 대신 쓸 수도 있습니다.
do
  echo -n "$a "
done
# "for" 루프의 [list] 를 만들기 위해서 명령어의 출력을 사용하는 예제

echo; echo


COUNT=80  # 'seq'에 변경할 수 있는 매개변수를 줘도 되는군요.

for a in `seq $COUNT`  # 혹은   for a in $( seq $COUNT )
do
  echo -n "$a "
done

echo

exit 0
run-parts

run-parts 명령어 [1] 는 대상 디렉토리에 들어 있는 모든 스크립트를 파일명을 아스키 순서대로 차례로 실행시켜 줍니다. 당연한 이야기지만 그 스크립트들은 실행 퍼미션이 걸려 있어야 됩니다.

crond 데몬/etc/cron.* 디렉토리에 들어 있는 스크립트를 실행 시키기 위해 run-parts 를 실행시킵니다.

yes

yes의 기본 동작은 y문자와 라인 피드를 표준출력으로 계속 뿌리는데 control-c로 멈출 수 있습니다. yes 다른 문자열처럼 하면 다른 문자열을 계속 뿌려 줍니다. 그러면 왜 이런 명령어가 필요할까요? 명령어 줄이나 스크립트 상에서 사용자 입력을 원하는 프로그램에게 yes의 출력을 재지향 시키거나 파이프를 통해 전달할 수 있습니다. 사실, 이 명령어는 expect의 간단한 버전입니다.

yes | fsck /dev/hda1 라고 하면 fsck를 한 방에 실행(non-interactive)시킵니다(조심하세요!).

yes | rm -r dirnamerm -rf dirname라고 한 것과 같은 효과를 갖습니다(조심하세요!).

주의

yesfsckfdisk 처럼 어쩌면 위험할 수도 있는 시스템 명령어에 파이프를 걸 때는 아주 조심해야 됩니다.

banner

주어진 인자를 아스키 문자로 이루어진(기본은 '#') 큰 수직 배너로 만들어 표준출력으로 출력해 줍니다. 프린터로 재지향시켜 실제로 출력해 볼 수도 있습니다.

printenv

특정 사용자의 모든 환경 변수들을 보여줍니다.

bash$ printenv | grep HOME
HOME=/home/bozo
	      

lp

lplpr은 파일을 프린트 큐로 보내서 프린터로 출력하기 위해 쓰이는 명령어입니다. [2] 이 명령어들의 이름은 옛날 라인 프린터(line printer)를 쓰던 시절에서 유래 됐습니다.

bash$ lp file1.txt 라고 하거나 bash$ lp <file1.txt

형식화된 출력을 얻기 위해 pr에서 lp로 파이프를 연결해서 자주 쓰입니다.

bash$ pr -options file1.txt | lp

groff이나 고스트스크립트(ghostscript)같은 형식화 패키지들은 자신의 출력을 lp로 직접 보내기도 합니다.

bash$ groff -Tascii file.tr | lp

bash$ gs -options | lp file.ps

관련 명령어로는 프린트 큐를 보기 위한 lpq나 프린트 큐에서 특정 작업을 지우기 위한 lprm이 있습니다.

tee

[유닉스는 수도업계에서 이 아이디어를 얻었습니다.]

이 명령어는 재지향 연산자이긴 하지만 약간 다릅니다. 수도 배관공의 tee(T자형 배관 파이프)처럼 단일 명령어나 파이프의 일부분으로 동작하는 명령어의 출력을 "빨아 들여" 다른 곳으로 동일한 결과를 복사해 내지만 결과에는 아무 영향을 미치지 않습니다. 진행중인 프로세스의 상황을 파일이나 종이로 출력할 때 유용한데, 보통은 디버깅 용도로 쓰여 중간 결과값을 추적하는데 쓰입니다.

                   tee
                 |------> 파일로
                 |
  ===============|===============
  명령어 --->----|-연산자--->---> 명령어의 결과
  ===============================
          

cat listfile* | sort | tee check.file | uniq > result.file
(check.fileuniq가 중복된 줄을 지우기 전의 상태인 cat listfile* | sort 까지의 상태를 갖고 있습니다.)

mkfifo

이름이 약간 애매한 이 명령어는 네임드 파이프(named pipe)를 만들어 냅니다. 네임드 파이프란 프로세스끼리 데이타를 주고 받을 수 있도록 하는 임시 first-in-first-out 버퍼를 가르키는 말입니다. [3] 전형적인 시나리오는 한 프로세스가 FIFO에 데이타를 쓰고 다른 프로세스는 그 데이타를 읽어 가는 것입니다. 예 A-10를 참고하세요.

pathchk

파일명이 올바른지 아닌지를 확인해 줍니다. 파일명이 최대 허용 가능 길이(255 글자)를 넘는다거나 자기 이름에 들어 있는 경로중 하나 이상의 디렉토리가 찾을 수 없다거나 할 경우에는 에러 메세지를 발생시킵니다. 불행하게도 pathchk는 알아 볼 수 있는 에러 코드를 리턴시키지 않기 때문에 스크립트 상에서는 전혀 쓸모가 없습니다.

dd

어딘지 모르게 불명확하고 약간은 쓰기 꺼려하는 "데이타 복사기"(data duplicator) 명령어입니다. 원래는 마그네틱 테이프를 이용해서 유닉스의 미니 컴퓨터와 IBM의 메인프레임간에 데이타를 교환하는데 쓰이던 유틸리티지만 아직도 그 쓰임새가 남아 있습니다. dd 명령어는 변환 과정을 거쳐 파일이나 표준입력/표준출력을 간단히 복사해 줍니다. 아스키/EBCDIC [4] 간 변환, 대소문자간 변환, 입출력간의 바이트 쌍을 바꾸거나 입력 파일의 처음이나 끝을 건너뛰거나 잘라내서 출력 파일을 만들어 내는 등의 변환이 가능합니다. dd --help 라고 하면 이 강력한 유틸리티가 처리할 수 있는 변환 목록과 다른 옵션을 볼 수 있습니다.

# 'dd' 연습하기.

n=3
p=5
input_file=project.txt
output_file=log.txt

dd if=$input_file of=$output_file bs=1 skip=$((n-1)) count=$((p-n+1)) 2> /dev/null
# $input_file 의 n 에서 p 까지 문자를 추출해 내기.




echo -n "hello world" | dd cbs=1 conv=unblock 2> /dev/null
# "hello world" 를 수직으로 에코시키기


# Thanks, S.C.

dd가 얼마나 다재다능한지를 보여주기 위해서 키보드 입력을 갈무리하는데 써 보겠습니다.

예 12-32. 키보드 입력을 갈무리하기

#!/bin/bash
# ENTER 없이 키누름을 갈무리하기.


keypresses=4                      # 갈무리할 키누름 수.


old_tty_setting=$(stty -g)        # 현재의 터미널 세팅을 저장.

echo "키를 $keypresses 번 누르세요."
stty -icanon -echo                # 캐노니컬(canonical) 모드 끄기.
                                  # 로컬 에코 끄기.
keys=$(dd bs=1 count=$keypresses 2> /dev/null)
# 'dd' 는 "if" 옵션이 없으면 표준입력을 씁니다.

stty "$old_tty_setting"           # 예전 터미널 세팅으로 복구.

echo "\"$keys\" 키를 눌렀습니다."

# S.C. 가 이 방법을 알려줬습니다.
exit 0

dd 명령어는 데이타 스트림에 대해 랜덤한 접근을 할 수 있습니다.
echo -n . | dd bs=1 seek=4 of=file conv=notrunc
# "conv=notrunc" 는 출력 파일이 잘리지(truncated) 않을 것이라는 옵션입니다.

# Thanks, S.C.

dd는 플로피나 테입 드라이브(예 A-5)같은 디바이스의 raw 데이타나 디스크 이미지에 직접 접근할 수 있기 때문에 보통은 부트 플로피를 만들 때 씁니다.

dd if=kernel-image of=/dev/fd0H1440

비슷하게, 심지어 "다른" OS 에서 포맷된 디스켓을 하드 디스크의 이미지 파일로 통째로 복사할 수 있습니다.

dd if=/dev/fd0 of=/home/bozo/projects/floppy.img

dd는 임시 스왑 파일을 초기화 하거나(예 29-2) 램디스크를 초기화하는데(예 29-3) 쓸 수 있습니다. 추천할만한 방법은 아니지만 전체 하드 드라이브 파티션을 저레벨로 복사해 낼 수도 있습니다.

아마도 시간이 남아도는 사람들은 dd를 어떻게 재미있게 쓸 수 있을지 계속 생각할 겁니다.

예 12-33. 파일을 안전하게 지우기

#!/bin/bash
# blotout.sh: 파일의 모든 기록 지우기.

#  이 스크립트는 대상 파일을 지우기 전에 
#+ 임의의 바이트들로 덮어쓰고, 0으로 덮어쓰기를 반복합니다.
#  이렇게 하고 나면, 디스크 섹터를 물리적으로 검사해도
#+ 원래의 파일 데이타를 찾아 낼 수 없습니다.

PASSES=7         # 파일 조각(file-shredding) 단계.
BLOCKSIZE=1      #  /dev/urandom 으로 I/O 를 할 때 필요한 유닛 블럭 크기.
                 #+ 이 크기가 지정되지 않으면 이상한 결과가 나옵니다.
E_BADARGS=70
E_NOT_FOUND=71
E_CHANGED_MIND=72

if [ -z "$1" ]   # 파일이 지정되지 않았음.
then
  echo "사용법: `basename $0` filename"
  exit $E_BADARGS
fi

file=$1

if [ ! -e "$file" ]
then
  echo "\"$file\" 파일을 찾을 수 없음."
  exit $E_NOT_FOUND
fi  

echo; echo -n "\"$file\" 파일을 정말로 완전히 지워 버리겠습니까(y/n)? "
read answer
case "$answer" in
[nN]) echo "하기 싫다구요?"
      exit $E_CHANGED_MIND
      ;;
*)    echo "\"$file\" 파일을 완전히 지우는 중.";;
esac


flength=$(ls -l "$file" | awk '{print $5}')  # 5 번째 필드가 파일 길이.

pass_count=1

echo

while [ "$pass_count" -le "$PASSES" ]
do
  echo "$pass_count 번째 단계"
  sync         # 버퍼 플러쉬.
  dd if=/dev/urandom of=$file bs=$BLOCKSIZE count=$flength
               # 파일을 임의의 바이트들로 덮어씀.
  sync         # 다시 버퍼 플러쉬.
  dd if=/dev/zero of=$file bs=$BLOCKSIZE count=$flength
               # 파일을 0으로 덮어씀.
  sync         # 또 다시 버퍼 플러쉬.
  let "pass_count += 1"
  echo
done  


rm -f $file    # 마지막으로, 온통 뒤섞이고 조각나 버린 파일을 삭제.
sync           # 마지막 버퍼 플러쉬.

echo "\"$file\" 파일이 완전히 삭제되었습니다."; echo


#  이 스크립트는 파일을 완전히 "조각내는데"(shredding) 비효율적이고 느린 방법을 
#+ 쓴 것만 빼면 정말 안전합니다. GNU "fileutils" 패키지중의 하나인
#+ "shred" 명령어도 똑같은 일을 하지만 좀 더 효율적입니다.

#  이 스크립트로 지워진 파일은 일반적인 방법으로는 "undelete"되거나
#+ 복구해 낼 수 없습니다.
#  하지만...
#+ 이런 간단한 방법은 과학적인 분석(forensic analysis)까지 막아내지는 못합니다.


#  Tom Vier의 "wipe" 파일 삭제 패키지는 이 간단한 스크립트보다 좀 더 
#+ 완전하게 파일을 지워 줍니다.
#     http://www.ibiblio.org/pub/Linux/utils/file/wipe-2.0.0.tar.bz2

#  파일 삭제와 보안에 대해서 좀 더 자세하게 알고 싶다면,
#+ Peter Gutmann의 논문을 참고하기 바랍니다.
#+     "Secure Deletion of Data From Magnetic and Solid-State Memory".
#           http://www.cs.auckland.ac.nz/~pgut001/secure_del.html


exit 0
od

od(8진 덤프, octal dump) 명령어는 입력(혹은 파일)을 8진수나 다른 진수로 변환해 줍니다. od는 바이너리 데이타 파일이나 /dev/urandom 같은 읽을 수 없는 시스템 디바이스 파일을 읽거나 처리하려고 할 때 필터로 쓸 수 있는 유용한 명령어입니다. 예 9-21예 12-10를 참고하세요.

hexdump

이진 파일을 16진수나 8진수, 10진수, 아스키로 덤프를 뜸. 이 명령어는 위에서 설명했던 od와 거의 비슷하지만 그렇게 쓸모가 있지는 않습니다.

m4

숨겨진 보물인 m4는 강력한 매크로 프로세서 [5] 유틸리티이나 거의 완전한 언어에 가깝습니다. m4는 사실 eval, tr, awk의 몇가지 기능들을 묶어 놓은 것입니다.

예 12-34. m4 쓰기

#!/bin/bash
# m4.sh: m4 매크로 프로세서 쓰기.

# 문자열
string=abcdA01
echo "len($string)" | m4                       # 7
echo "substr($string,4)" | m4                  # A01
echo "regexp($string,[0-1][0-1],\&Z)" | m4     # 01Z

# 산술식
echo "incr(22)" | m4                           # 23
echo "eval(99 / 3)" | m4                       # 33

exit 0

주석

[1]

사실 이 명령어는 데비안 리눅스 배포판에서 쓰던 것입니다.

[2]

프린트 큐란 프린트를 위해 "순서대로 대기"하고 있는 작업 그룹을 말합니다.

[3]

Andy Vaught가 리눅스 저널에 1997년 9월에 기고한 Introduction to Named Pipes란 아주 훌륭한 문서를 참고하세요.

[4]

EBCDIC (발음은 "엡시딕", ebb-sid-ic) 은 Extended Binary Coded Decimal Interchange Code 의 첫 글자만 딴 두문자어입니다. IBM 의 데이타 포맷으로 더 이상 쓰이지 않습니다. conv=ebcdic 옵션의 이상한 응용으로는 아주 빠르고 간단하지만 그렇게 안전하지는 않은 텍스트 파일 인코더로 쓸 수 있습니다.
cat $file | dd conv=swab,ebcdic > $file_encrypted
# 인코드(언뜻 봐서는 알아볼 수가 없습니다).		    
# 더 이해하기 힘들게 해려면 바이트를 스왑(swab)시킬 수도 있습니다.

cat $file_encrypted | dd conv=swab,ascii > $file_plaintext
# 디코드.

[5]

매크로는 명령어 문자열이나 매개변수에 따라 여러가지 연산으로 확장되는 심볼릭 상수입니다.