Information Security Study
240118 셸 스크립트(source와 .의 차이, .profile, 전역 경로, 변수, quote, 치환, 파라미터, 제어문, $?) 본문
240118 셸 스크립트(source와 .의 차이, .profile, 전역 경로, 변수, quote, 치환, 파라미터, 제어문, $?)
gayeon_ 2024. 1. 18. 15:54source와 . 실행의 차이점 살펴보기
test1.sh 를 생성해주세요
#!/bin/bash
poiuqwer /
이제 위의 셸 스크립트는 poiuqwer라는 사전에 지정된 적이 없는 리눅스 명령어를 실행한다.
이를 위해서 alias에서 별명을 등록해야 한다.
$ alias poiuqwer=’ls -alF’
먼저 해당 명령을 실행하면 이제 poiuqwer은 ls -alF을 대체해서 사용할 수 있다.
단 source명령어는 해당 sh파일을 수행하지만
. 명령어는 해당 sh파일을 수행하지 못한다.
권한을 부여했는데도 실행하지 못한다.
. 명령어로 sh파일을 수행하지 못하는 이유
- source 명령어는 현재 셸에서 실행하도록 되어있지만
- .명령어는 새로운 셸을 하나 더 순간적으로 열어서 실행하는데
- 이 때, 새로 연 셸에는 alias 설정이 되어있지 않기 때문이다.
셸 스크립트의 배치
셸 스크립트를 여기저기 중구난방으로 작성하면 해당 경로를 찾아들어간 다음
그곳에서
$ ./파일명.sh
와 같은 형식으로 실행해야 하는데 파일이 많아지면 많아질 수록 관리하기 힘들어진다.
따라서 이를 방지하기 위해서 셸 스크립트들을 모아두는 디렉터리를 하나 작성한 다음
그곳에 작성된 셸 스크립트들은 어디서건 그냥 파일명만 적어도 실행되게 만든다면 편할 것이다.
먼저 모든 셸 파일들을 저장할 수 있는 폴더를 만든다.
편의상 ~ 경로 하위에 bin 폴더로 만들 것이다.
$ mkdir ~/bin
그리고 셸 스크립트 파일들을 전부 옮긴다.
$ mv 파일명.sh ~/bin
ls로 ~/bin 하위 스크립트를 확인해보면 세 파일 모두 잘 이동했다.
(mv *.sh로도 셸 스크립트를 모두 옮길 수 있다.)
그리고 검색경로에 ~/bin을 추가하기 위해
vim으로 ~/.profile 파일을 열어 다음과 같이 추가한다.
PATH="$PATH:~/bin"
위의 명령어는 환경변수 PATH에 ~/bin을 추가하는 것이다.
파일 마지막에 추가한다.
$ source ~/.profile
을 실행하면 즉시 적용된다.
(파일명 앞에 .이 붙은 건 보이지 않는 파일)
이제 해당 작업을 수행한 계정 기준으로는 어디서건 .sh 파일을 호출할 수 있다.
$ 파일명.sh
를 입력하면 어떤 경로에서건 해당 파일을 ~/bin 폴더에서 읽어와 실행한다.
(PATH를 추가해서 가능한 것이다.)
전역 경로 비활성화
$ ls homesize.sh
는 현재 경로에 homesize.sh가 없다면 실행되지 않는다.
그러나
$ source homesize.sh
는 실행된다.
따라서 source 명령어를 쓸 때는 해당 단축경로를 비활성화 하고 싶다면
$ shopt -u sourcepath
를 수행하면 된다.
source 명령어로 셸스크립트를 수행하는 건 불가능하지만
profile 내부에 어디에서든지 호출할 수 있도록 환경변수를 설정했기 때문에
여전히 그냥 파일명으로만 호출하는 건 가능하다.
여러 줄 셸 스크립트 작성하기
실제로는 셸 스크립트는 여러줄로 된 스크립트를 작성하는것이 일반적이다.
여러 줄 명령어를 순차적으로 실행시킬 때는 기본적으로 엔터키를 눌러 개행시키면
순차적으로 하나씩 명령을 실행한다.
rootls.sh
#!/bin/bash
echo "root"
cd /
ls -l
위 명령어는 루트디렉토리의 상세정보를 보여준다.
엔터키로 개행시키는 대신 ;을 statement(문) 하나하나마다 붙여줘도 잘 동작한다.
#!/bin/bash
echo "root";cd /;ls -l
가운데 빈 칸은 실행 구문으로 취급되지 않으니 공백을 많이 줘서 가독성을 높여도 된다.
화면상으로는 개행이지만 한 줄로 연결하기
일부 프로그래밍 언어에서는 문자열 등이 길때 역슬래시\ 를 붙이면
명목상으로는 여러줄이지만 실제로는 한 줄로 붙여주는 역할을 한다.
즉, 개행 무시 문자라고 볼 수 있다.
asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf
asdfasdfasdfasdfasdfasdfasdfasdfasdf\
asdfasdfasdfasdfasdfasdfasdfasdfasdf\
asdfasdfasdfasdfasdfasdf
위 2개는 같은 길이의 문자로 간주된다.(\를 쓴 부분은 개행이 일어나도 없는것으로 간주)
셸 스크립트에서도 동일한 맥락으로 \를 사용할 수 있다.
rootdir.sh
#!/bin/bash
echo \
"root"
위의 \로 인해 저 명령어는 ehco; “root”; 의 2개의 문이 아닌 하나의 문인 echo “root”로 간주된다.
\는 셸에서 명령어를 칠 때도 다음줄 내용을 이어서 쓴다는 의미로도 사용된다.
비슷하게 사용되는 파이프라인 문자 | 역시 개행을 무시하고 같은 명령어로 간주한다.
주석
# 을 붙이면 #의 오른쪽은 전부 주석으로 처리된다.
변수
셸 스크립트도 일종의 스크립트 구문이므로 변수 등을 사용할 수 있다.
셸 내부에서 사용하는 변수를 셸 변수라고 한다.
이런 변수는 저장시에는
변수명=저장할내역
형식으로 사용하며
이후 변수 내부 내역을 꺼낼 때는
$변수명
형식으로 해당 변수의 값을 참조해서 쓸 수 있다.
명령프롬포트에서도 바로 사용 가능하다.
변수의 값이 아닌 $abcd 문자 자체를 출력하고 싶다면 $ 앞에 탈출문자를 붙여주면 된다.
quote 이해하기
quote는 따옴표다.
홑따옴표는 변수값을 포매팅에 활용할 수 없지만
쌍따옴표는 변수값을 포매팅에 활용할 수 있다.
quotetest.sh
#!/bin/bash
name=chad
echo 'my name is $name'
echo "my name is $name"
위의 코드는 echo가 그냥
my name is $name 을 출력하지만
아래의 코드는
my name is chad 라고 $name이 따옴표 내부에서도 변수 값을 참조해온다.
쌍따옴표 내부에서 변수값을 참조하지 않고 $name으로 출력하고 싶다면
\$name 과 같이 탈출문자 \을 활용해 참조목적으로 쓰인 $가 아님을 지정하면 된다.
명령어 치환
특정한 명령어를 수행한 결과를 파일 이름 등으로 사용하고 싶은 경우가 있다.
이를 수행할때는 $(명령어)를 이용할 수 있다.
$ date ‘+%Y-%m-%d’
수행시 yyyy-mm-dd 형식으로 날짜가 나오고 이것을 파일 이름으로 둬야 하는 경우가 있다.
이 때, 날짜는 파일이름으로 만들고 싶다면 아래와 같이 셸 스크립트를 작성한다.
today_date.sh
#!/bin/bash
filename=$(date '+%Y-%m-%d')
touch "$filename"
셔뱅을 제외한 첫 번째 줄은 filename변수에
우변의 구문을 실행했을때 나오는 날짜를 변수에 저장한다.
그리고 touch를 통해 날짜로 파일을 생성한다.
즉, 콘솔에 찍혔을 값을 우변에 리턴하는 것이다.
오늘 날짜로 생성된 파일이 생겼다.
파라미터 전달하기
셸 스크립트에서는 파라미터의 위치에 따라 $번호 에 해당하는 변수에 대입된다.
$ ./parameters.sh aaa bbb ccc
위 구문에서 첫 번째로 사용한
./parameters.sh 는 자동으로 $0으로 간주되고
그 다음인 aaa는 $1
그 다음인 bbb는 $2
그 다음인 ccc는 $3
에 저장되는 식으로 파라미터 순서를 왼쪽부터 0으로 간주한다.
parameters.sh
#!/bin/bash
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
echo "\$5 = $5"
위와같이 $번호 = 값 을 5번까지 출력하는 파일을 먼저 작성한다.
그리고 아래와 같이 실행하면
$ ./parameters.sh aaa bbb ccc
순서대로 파라미터 값이 들어간 것을 확인할 수 있다.
인자 개수 확인하기
이렇게 전달된 인자의 개수가 몇 개인지는 $# 으로 조회할 수 있다.
인자의 개수를 출력할 수 있도록 수정한 후
다시 실행하면 마지막에 인자의 개수가 잘 출력된다.
제어문 사용하기
배시에서도 조건분기, 반복처리를 기술할 수 있다.
개념적으로는 완전히 같고 문법만 약간 다르다.
if문
if문은 [ 명령어 ] 의 내용이 true로 판단되면 실행할 내용,
그리고 else나 elif가 있다면 첫 줄이 false로 판단되면 실행할 내용을 적는다.
if_bin.sh
#!/bin/bash
if [ "$1" = "bin" ]; then
echo "OK"
else
echo "NO"
fi
위 스크립트에서 $1은 위에서 배운 파라미터의 $1이다.
파라미터로 뭘 주냐에 따라 결과가 다르게 나온다.
또한 세미콜론이 있다면 then과 조건식이 같은줄에 놓일 수 있지만
생략했다면 무조건 다음줄로 내려야 한다.
작성 시 주의사항
- 대괄호 양옆으로 한 칸씩 띄어서 작성해야 한다.
- if문 마지막에는 fi를 작성해야 한다.
위와 같이 작성한 뒤
셸 스크립트 파일명 옆에 파라미터를 입력하면
bin과 일치하는지 검사 후 OK나 NO를 출력한다.
명령어의 종료 상태
$ ls # ls명령어를 실행함
$ echo $? # 직전에 실행한 명령어(지금은 ls)의 상태를 조회함
0
기본적으로 명령어는 정상 종료 시 0을 가진다.
그러나 에러가 발생하면 0이 아닌 다른 번호를 가지게 된다.
showstatus.sh
#!/bin/bash
ls /
echo "exit ($?)"
ls /test
echo "exit ($?)"
아마 없는 경로를 지정한 2번째 ls는 다른 번호를
root 폴더 조회인 첫 ls는 0이 나올 것이다.
ls /은 루트 디렉토리 내의 파일, 디렉토리 목록이 출력되고
ls /test는 루트 밑 test 디렉토리 내의 파일, 디렉토리 목록을 출력하라는 명령어다.
셸 스크립트 작성 후 권한을 부여한 뒤 실행해 보면
없는 경로를 입력한 두 번째 ls는 0이 아닌 2가 뜨는 것을 확인할 수 있다.