제 5 장 본쉘 특징
---------------------
1. 쉘 환경
2. 명령행
3. 메타문자
4. 파일이름 치환
5. 변수
6. 인용부호
7. 명령어 치환
8. 함수
9. 리다이렉션
10. 파이프
11. here 문서
---------------------
■ 쉘의 특성 리다이렉션(Redirection) 파이프(pipe) 셀 자체의 기능(bash function) 변수(Variable) 메타캐릭터(Metacharacter) 히스토리(History) 환경파일(Environment Files) ■ 리다이렉션(Redirection) 방향재설정 fd(file description, 파일기술자) 열린 파일을 구분할때 사용하는 기호 (ex) vi file1 <-- 운영체제가 번호 할당 fd5) ------------------------- 표준입력 [stdin (keyboard)] 표준출력 [stdout(screen)] 미리 예약 되어 있는 fd값 : fd0,fd1,fd2 다른 프로세스에 할당 할 수 없다. 표준에러 [stderr(screen)] ------------------------- fd0 -> shell -> fd1 -> fd2 원본파일을 3개 열면 무엇으로 구분할까? =>파일을 열면 fd 값을 할당해준다. 예로 int fd=open(...) 같이 int형 fd에 fd값을 넣는것. # ls /var /nodir # cat 지정이 없으면 하나만쳐도 두개씩 나오는 이유 hello stdin 지정이 없으니 키보드로 입력받고 hello stdout 지정이 없어서 키보드로 출력한다. linux linux <CTRL + D> # cat < /etc/passwd (< : 입력 redirection) 키보드 대신 /etc/passwd로 입력을 받는다. (키보드입력 파일출력) # cat > file1 (> : 출력 redirection) 정상적인 출력결과를 file1에다 집어넣어라. (파일입력 파일출력) hello linux <CTRL + D> # cat file1 입력 리다이렉션(Redirection stdin) # CMD < file1 # CMD 0< file1 (X) # CMD << file1 (EX: Here Documentation) # cat < /etc/passwd 1. # mail -s "Test Mail" root@example.com < report.txt 명령어로 report파일을 메일로 전송 report.txt = mail text body 2. # mail root Subject: Test Mail This is a test. <.> or <CTRL + D> # 출력 리다이렉션(Redirection stdout) 정상적인 메시지 지정없으면 모니터에 출력하는데 다른쪽에 출력할때 사용 방향재설정 # CMD > file1 # CMD 1> file1 # CMD >> file1 # CMD 1>> file1 # ls -l > file.list # cat file.list echo => 모니터 출력 # echo 1111 > file1 # echo 2222 > file1 # cat file1 # echo 3333 >> file1 # cat file1 [TERM1] # tty /dev/pts/3 [TERM2] # echo hello redirectioin > /dev/pts/3 # cat dic1.txt dic2.txt > dic3.txt 에러 리다이렉션(Redirection stderr) 에러메시지를 다른쪽에 출력할때 사용, 생략하면 1번하고 동일해져서 생략불가능 # CMD 2> file1 # CMD 2>> file1 # ls -l /test /nodir 정상 출력 에러 출력 # ls -l /test /nodir > file1 에러 출력 file1 => 정상 출력 # ls -l /test /nodir 2> file2 정상 출력 file2 => 에러 출력 # ls -l /test /nodir > file1 2> file2 file1 => 정상 출력 file2 => 에러 출력 # ls -l /test /nodir > file1 2>&1 file1 => 정상 출력 , 에러 출력 키워드 지정된 것 외에는 함수를 끌어 사용해야 한다. 쉘스크립트는 전부다 명령어를 끌어다 사용한다. 일반 API는 그 기능만 동작하지만 명령어는 옵션을 주면 다른식으로 동작하기 때문에 굉장히 많은 용도로 사용가능하다. # script.sh > file.log 2>&1 <= 정상적인 메시지와 에러메시지를 한꺼번에 담아줘야 적절한 로그기능이 가능 $ find / -name core -type f 2>/dev/null <= 관리가 권한이 아니라 열어볼 수 없는 파일이 많이 생긴다. null처리(에러출력삭제) # ./configure --prefix=/usr/local/apache2 >apache.log 2>&1 <=점검하는 중요한 과정이라 점검에서 오류가 나면 문제가 커진다. 정상출력과 에러메시지를 저장하여 파일로 생성, 이 파일을 분석하면 도움이 된다. ■ 파이프(Pipe) # CMD | CMD # ps -ef # more /etc/passwd # ps -ef | more # ps -ef | more # ps -ef > ps.txt 효율적 # more ps.txt # CMD | more # ps -ef | more # ps -ef | grep xinetd => Pipe의 정의 때문에 입력값을 지정안해줘도 된다. [참고] CMD | tee file1 =>tee명령어 유용하다. file1에도 저장하고 모니터에도 출력한다. # cal > cal.txt # cal | tee cal2.txt 여러개의 터미널(화면)을 공유하는 경우 [참고] script CMD # cd /test # script -a file.log => Sub shell이 뜨고 여기서 작업한 명령어 출력값을 file.log로 저장된다. # ls ; date ; cal # exit # cat file.log => 선배들 컴퓨터 몰래 script실행해서 어떤작업했는지 확인도 가능하다..! [TERM1] # tty /dev/pts/3 [TERM2] # tty /dev/pts/4 [TERM3] # script -a /dev/null | tee /dev/pts/3 | tee /dev/pts/4 # CMD # exit => Sub shell생성하여 넘기면 tee에서 모니터와 pts/2에 출력하고 넘긴것을 tee가 받고 다시 모니터와 pts/4에 출력 모니터링 구문을 사용하는 경우(로그 생성) # while true > do > clear > echo "-------------'date'----------------" > df -h > sleep 2 > done -------------'date'---------------- Filesystem Type Size Used Avail Use% Mounted on /dev/mapper/VolGroup00-LogVol00 ext3 37G 3.4G 32G 10% / /dev/sda1 ext3 99M 13M 82M 14% /boot tmpfs tmpfs 506M 0 506M 0% /dev/shm -------------'date'---------------- Filesystem Type Size Used Avail Use% Mounted on /dev/mapper/VolGroup00-LogVol00 ext3 37G 3.4G 32G 10% / /dev/sda1 ext3 99M 13M 82M 14% /boot tmpfs tmpfs 506M 0 506M 0% /dev/shm -------------'date'---------------- ...(중략).... # ls -l /test /nodir | tee file2.log # ls -l /test /nodir | tee file2.log 2>&1 (X) # ls -l /test /nodir 2>&1 | tee file2.log ■ 쉘 자체의 기능(bash shell function) # set -o 기능목록 출력 # set -o vi vi 기능 off # set +o vi vi 기능 on ■ 변수(Variable)***중요 변수의 종류 - 지역변수(# VAR=5) - 환경변수(# export VAR=5) (전역변수, export차이로 지역변수 환경변수를 나눔) - 특수변수($$, $?, $!, $*, $1, $2, ....) (뒤에 있는 것이 변수이름) 변수 선언 방법(env/set) # VAR=5 (# export VAR=hello) 변수이름=값(=양쪽에 공백이 있으면 안된다.) # export VAR # echo $VAR (# printf $VAR) # unset VAR ※ 변수를 생성할때는 $를 붙히지 않고 변수를 사용할때는 $를 붙힌다. 프로그램 내에서 사용했던 변수는 새로운 서브 쉘을 가지고 있는다. 변수를 해제하지 않아도 사용했던 프로그램을 종료하면 사라진다. 변수의 export 의미 # ps # VAR1=5 # echo $VAR1 # bash # ps PID TTY TIME CMD 7098 pts/4 00:00:00 bash 8240 pts/4 00:00:00 bash 8254 pts/4 00:00:00 ps # echo $VAR1 # exit # echo $VAR1 # VAR2=10 # export VAR2 (# export VAR2=10) # echo $VAR2 # bash # echo $VAR2 # exit # echo $VAR2 => export하여서 Sub Shell에도 환경변수를 확인할 수 있다.
특수변수
$$ , $!, $?
$*, $#, $0, $1, $2, $3, ....
$$ : 현재 쉘의 PID 번호 저장(EX: 임시 파일 생성, /tmp/.tmp.$$)
$! : 바로 이전 수행된 백그라운드 프로세스의 PID 번호 저장 (bg프로그램을 kill 명령으로 종료할 때)
$? : 바로 이전 수행된 명령어의 return value 저장(0 ~ 255)
값이0 정상 종료 /0이 아닌 값 : 비정상 종료
$* : 모든 인자($* == $@)
$# : 인자의 개수 (#의 숫자의 의미라서)
$0 : 프로그램 이름
$1 : 프로그램에 대한 첫번째 인자
$2 : 프로그램에 대한 두번째 인자
$3 : 프로그램에 대한 세번째 인자
임시파일 생성하는 경우
# echo $$
# ps
# touch /tmp/tmp.$$
# ls -l /tmp/tmp.*
바로 이전에 수행된 백그라운드 프로세스를 종료하는 경우
# sleep 20 &
# echo $!
# sleep 200 &
# kill $!
명령어의 정상 동작 유무 확인하는 경우
# date
# echo $?
0 <= c언어와 다르게 0이 참이다
# cd /nodir
# echo $?
1~255 <= Error인 경우 0이 아닌 1~255숫자
# llllssss
# echo $?
# vi script.sh 값이 0이면 참 , 0이 아니면 거짓
---------------------------------
#!/bin/bash
NET=172.16.10
START=200
END=230
while [ $START -le $END ]
do
ping -c 1 $NET.$START > /dev/null 2>&1
if [ $? -eq 0 ] ; then
echo "$NET.$START is alive"
else
echo "$NET.$START is die"
fi
START=`expr $START + 1`
done
arp -an | grep $NET | grep -v incomplete
---------------------------------
인자 변수
# cd /test
# vi test.sh
-------------------------------
#!/bin/bash
echo $* => 인자 전체인 file1 file2 출력
echo $# => 인자의 갯수인 2 출력
echo $0 => 프로그램 이름 ./test.sh
echo $1 => 첫번째 인자 file1
echo $2 => 두번째 인자 file2
-------------------------------
# chmod 755 test.sh
# ./test.sh file1 file2
# alias pps='ps –ef | head –1 ; ps –ef | grep $1'
# pps xinetd
# pps java => 첫번째 인자값이 변수안에 들어가서 수행된다.
# pps httpd
# alias nstat='netstat -an | head -2; netstat-an | grep $1'
■ 쉘 메타캐릭터(Shell Metacharacter) 의미가 있는 특이한 문자들
‘’, “”, ``, \, ;
‘’: 작은따옴표(single quotation) 이안을 해석하지 말아라! 문자열처리가 된다.
# echo $SHELL
/bin/bash
# echo '$SHELL'
$SHELL
# echo hello linux
# echo hello linux
# echo 'hello linux'
“”: 큰 따옴표(double quotation) => $, ``(백쿼터), \ 이 세가지는 해석이 되고 나머지는 해석이 되지 않는다.
# echo "$SHELL"
/bin/bash
# echo "$SHELL is /bin/bash."
/bin/bash is /bin/bash.
`` : 역 따옴표(back quotation) sh -> ksh -> zsh -> bash => 쉘은 커맨드라고 생각하고 실행한다.
# date
(sh style) # echo `date` Born Shell에서 나와서 기능을 포함하고 있어서 모두 동작한다.
2015. 11. 06. (금) 12:41:42 KST
(ksh style) # echo $(date) Korn Shell에서 나와서 Bash는 기능을 포함하고 있어서 모두 동작한다.
2015. 11. 06. (금) 12:41:42 KST
# echo "`date` is time."
2015. 11. 06. (금) 12:42:47 KST is time.
\ : 백 슬래쉬(back slash) => 바로 뒤에 오는 것을 해석하지 말아라
# echo $SHELL
# echo \$SHELL 그냥 $기호가 된다.
$SHELL
# find / -type f –exec grep –l Error {} \; => 3가지 유형 전부 해석이 안되고 단순한 ; 이 되는 것이다.
# find / -type f –exec grep –l Error {} ';'
# find / -type f –exec grep –l Error {} ";"
# alias rm='rm -i'
# rm -r dir1
# \rm -r dir1
alias vi='usr/bin/vim' 으로 잘못된 alias 설정 후
# vi ~/.bashrc 하면 잘못된 vi alias가 실행되어 문제가 발생한다
이것을 해결하기 위해
\vi ~/.bashrc 같은 방법을 사용한다
[참고] \CMD & CMD\ \가 앞에오면 뒤에를 해석하지 말아라, \가 뒤에오면 명령어가 아직 끝나지 않았다는 뜻
# ./configure \ Shell 이 \를 끝에서 만나면 아직 끝나지 않았구나 생각하고 다음줄을 해석한다.
> --prefix=/usr/local/apache2 \
> --options1=.... \
> --options2=....
■ ; : 세미콜론(semicolon)
# date
# cal
# ls
# date ; cal ; ls
# ps –ef | grep xinetd
# ps –ef | head –1
# ps –ef | head –1 ; ps –ef | grep xinetd
# alias pps='ps –ef | head –1 ; ps –ef | grep $1'
# EDITOR=/usr/bin/vi ; export EDITOR
# export EDITOR=/usr/bin/vi
■ 엘리어스(Alias) ※=> '=' 양쪽에 공백이 있으면 안된다.
# alias ls='ls --color'
# alias ls
# unalias ls
■ 함수
# a() { ls ; date ; cal ; } => 함수 선언 방식
# a => 함수 실행 방법
# typeset -f => 함수 확인 방법
# unset –f a => 함수 해제 방법
[참고] CMD -> alias -> function -> script
■ 환경파일(EX: bash)
/etc/profile
~/.bash_profile
~/.bashrc
■ 기타(Here Documentation) cat CMD + Here Documentation
cat << EOF 다음 명령어에 같은EOF를 쓸 때까지, EOF사이에 명령어를 모두 모니터에 출력
프로그램 작성시 사용.
root[/test]#./test.sh echo ".." |
EOF = echo 구문을 대신하여 사용
cat << EOF |
# vi test.sh
-------------------------------
#!/bin/bash
cat << __EOF >> /tmp/.file1.c <= __EOF에 A라고 쓰면 아래 __EOF에 A가 들어오면 가운데 내용이 입력결과에 추가되는 형태, 키보드에서 치는 것을 대신하고 출력은 모니터에 한다.
#include <stdio.h>
main()
{
printf("hello");
}
__EOF
gcc -o /tmp/.file1 /tmp/.file1.c
/tmp/.file1
-------------------------------
기본예제
# cat << EOF
> hello linux
> hello linux
> hello linux
> EOF
-------------------------------
hello linux
hello linux
hello linux
echo " hello linux"
echo "hello linux"
echo " hello linux"
와 같이 사용해야하는데 이것을 편리하게 사용할 수 있는 방법이다.
목록이나 긴 형태 장문에서 유용하게 사용할 수 있다.
■ 기타(쉘 메타캐릭터 (연속))
그룹화(Grouping)
( ls ; pwd ; date ) > outputfile <= 그룹형태로 묶어줘야 3개의 커멘트 실행결과가 들어간다.
# mkdir /disk1 /disk2
# cd /disk1
# cp -p /etc/passwd file1
# ln -s file1 file2
# mkdir dir1
# cp file1 file3 ; chown user01 file3 ; chmod 777 file3
# touch .file1
# ls -al
# cp -r /disk1/* /disk2 (X)
# ls -al /disk1
# ls -al /disk2
# rm -rf /disk2 ; mkdir /disk2
# cd /disk1
# tar cvf - . | (cd /disk2 ; tar xvf -)
# find / \( -perm -4000 -o -perm -6000 \) -type f
조건부 실행
sh style) -o(OR), -a(AND)
ksh style) &&, ||(double pipe)
bash style) 당연히 두 방법 모두 사용 가능
# cd /etc && ls -l passwd 아래 if구문을 짧게 사용할 수 있다.
if cd /etc ; then <- then 참일 때 / 거짓 일 때는 then이 수행되지 않음.
ls -l passwd
fi
# [ -f /etc/passwd ] && echo "OK"
if [ -f /etc/passwd ] ; then
echo "OK"
fi
# [ $? -eq 0 ] && echo "OK"
if [ $? -eq 0 ] ; then
echo "OK"
fi
# [ -f /etc/passwd ] && echo "OK" || echo "Fail" <= || 연산은 앞이 참이면 실행안되고, (&&가 먼저 수행된다. &&가 거짓일때 ||가 수행된다.)
거짓이면 뒤에 부분이 참/거짓을 판별해야 되기 때문에 실행된다.
if [ -f /etc/passwd ] ; then || 는 &&와 || 둘중 하나라도 참이면 참이라고 해석한다. 따라서
echo "OK" &&가 참일시 ||는 수행되지 않는다.
else
echo "Fail" &&가 거짓일시 ||는 반드시 수행되어야 한다.
fi 앞 명령어가 참이기 때문에 &&가 아니라면 ||이 참이된다.
# cd /test ; rm -rf * (# cd /test ; rm -rf /test/*) <= 만약 test디렉토리가 없으면 이동하지 못하고 현재 폴더를 다지움
cd /test가 거짓일시 뒤에 명령어가 그대로 수행한다 ( /test에 가기전 pwd에 모든 파일을 삭제)
# cd /test && rm -rf * (# cd /test && rm -rf /test/*) <= test디렉토리로 이동할 수 있는 경우만 이동해서 삭제한다 *안전
cd /test가 거짓이면 뒤에 삭제 명령어가 수행 되지 않는다.
파일의 이름/경로(dirname/basename)
# DIR1=/etc/sysconfig/network-scripts/ifcfg-eth0
# dirname $DIR1 <= 파일을 제외한 경로 이름
# basename $DIR1 <= 경로를 제외한 파일 이름
# echo $(dirname $DIR1)$(basename $DIR1) $() = ' '(백쿼터)동일하다
/etc/sysconfig/network-scriptsifcfg-eth0
val CMD + other CMD
) -->
# cat script.sh
#!/bin/ksh ) --> USERNAME=root (X) sed 's/$USERNAME/ROOT/g' /etc/group => ' '을 사용해서 $USERNAME스트링으로 인식한다. (0) eval sed 's/$USERNAME/ROOT/g' /etc/group => $USERNAME에 root라고 넣고 또한번 더 해석한다. |
# cat script.sh
#!/bin/ksh ) --> USERNAME=root (0) sed "s/$USERNAME/ROOT/g" /etc/group => " "은 $기호를 인식한다. |
) -->
# A=5
# B=3
# echo $A
# echo $B
) -->
# A=B
# B=3
# echo $A ---> B
# echo $B ---> 3
# echo $$A (# echo $B) --->엉뚱한 결론
# eval echo \$$A ---> 두번 해석하여 3이라는 값이 나온다.
A=B
$A => B
$B => 3
\$$A => echo '$B'
eval \$$A => $B => 3
# ls -a -t -r
# A='ls -a'
# B=' -t -r'
# echo $A
# echo $B
# `$A$B` ----> ls -a -t -r (x) 쉘이 한번해석
ls -a -l -t -r
A='ls -a --color'
B='-t -r'
C=eval $A $B
$C
'Learning > └◆Shell Scripts' 카테고리의 다른 글
[Shell Scripting] 본(Born) 쉘 프로그래밍_2 (0) | 2016.12.12 |
---|---|
[Shell Scripting] 본(Born) 쉘 프로그래밍 (0) | 2016.12.12 |
[Shell Scripting] 유닉스 명령어(4) 기타 명령어 (0) | 2016.12.12 |
[Shell Scripting] 유닉스 명령어(3) awk CMD (0) | 2016.12.12 |