Bash 팁 정리
내가 참조하기 위한 Bash 팁 정리
Shell 관련 도움말 얻기
- 아래와 같이 실행하면 GNU core util의 도움말을 볼 수 있다.
$ info coreutils
- Bash 관련 도움말은 아래와 같이 얻을 수 있다.
$ man bash $ info bash
- 아래와 같이 실행하면 사용하는 쉘의 간단한 도움말이 출력된다.
$ help
특정 bash function의 도움말은 아래와 같이 얻을 수 있다.
$ help <bash_function>
Sell script 정적 검사 툴
- ShellCheck
다음과 같이 설치할 수 있다.$ sudo apt install shellcheck
이후 아래와 같이 검사할 수 있다.
$ shellcheck <shell_script_파일이름>
또는 설치할 필요없이 ShellCheck 온라인 페이지를 이용할 수도 있다.
Shell script 포매팅
- 아래와 같이 shfmt 툴을 설치한다.
$ sudo apt install shfmt
또는 아래와 같이 설치할 수도 있다.
$ curl -sS https://webinstall.dev/shfmt | bash
shfmt 툴을 이용하여 아래 예와 같이 포매팅할 수 있다.
$ shfmt -w -sr -fn <shell_script_파일이름>
- VS Code 용 shell-format 익스텐션을 이용하면 편리하다.
- 스타일 가이드 참조: Shell Style Guide (by Google)
Shebang
Bash 스크립트 파일에서 첫 줄은 아래와 같은 shebang(#!
)으로 시작하는 것이 좋다.
#!/bin/sh
이는 특히 스크립트 파일의 확장자가 sh가 아닌 경우에 유용한데, 이렇게 shebang으로 인터프리터를 shell로 지정하면 에디터에서 shell script로 인식하여 syntax 하이라이팅이 잘 된다. 또, 파일에 실행 권한을 주면 인터프리터 명시없이 파일 이름만으로도 실행시킬 수 있게 된다.
Argument 처리
- 아규먼트 개수는
$#
로 얻을 수 있다. - 각 아규먼트는
$0
,$1
,$2
등으로 얻을 수 있다. - 모든 아규먼트는
$@
로 얻을 수 있다. - 예제
if [ $# != 2 ]; then echo "Usage: $0 arg1 arg2" exit 1 fi
Bash $ 처리
- Shell 스크립트에서는
$
가 특수 용도로 사용되는데,$
문자 자체를 표현하기 위해서는$$
와 같이 사용하면 된다. - 만약
$$
문자열을 표현하려면\$\$
와 같이 쓰면 된다.
Shell 세팅
현재 자신이 사용하고 있는 shell은 아래와 같이 확인할 수 있다. (/etc/passwd 파일에 있는 정보임)
$ echo $SHELL
Shell 변경은 아래와 같이 change shell 명령어로 할 수 있다.
$ chsh -s <shell 경로>
Shell prompt 변경
Shell prompt는 PS1 환경 변수에 세팅하면 된다.
예를 들어 아래 내용을 ~/.bashrc
파일에 추가하면 적용된다. (\u
는 user ID, \h
는 host name, \w
는 working directory를 나타냄)
export PS1="\[\033[01;32m\]\u\[\033[00m\]:\[\033[01;94m\]\W\[\033[00m\]\$ "
단축 명령(alias) 지정하기
- 아래와 같이 별명을 지정할 수 있다.
$ alias 별명이름='내용'
이후 아래와 같이 별명이름으로 실행시킬 수 있다.
$ 별명이름
- 별명이름의 실제 내용 확인은 아래와 같이 할 수 있다.
$ alias 별명이름
Chip thread 개수 얻기
- /proc/cpuinfo 파일이나 lscpu 명령을 이용하여 얻을 수 있다.
- 더 간단한 방법은 아래와 같이
nproc
명령을 이용하여 얻는 것이다.$ nproc
또는
$ echo $(nproc)
Shell 명령의 결과가 표시되지 않게 하기
- 표준 입력/출력/에러의 파일 디스크립터 값
0
: 표준 입력 (stdin)1
: 표준 출력 (stdout)2
: 표준 에러 (stderr)
- 표준 출력을 표시하지 않는 예제
$ cat test > /dev/null $ cat test 1> /dev/null
- 표준 에러를 표시하지 않는 예
$ cat test 2> /dev/null
- 표준 출력과 표준 에러를 모두 표시하지 않는 예제
(표준 출력을 null로 리다이렉션하고, 표준 에러(2)도 표준 출력(1)으로 리다이렉션 시킴)$ cat test 1> /dev/null 2>&1 $ cat test > /dev/null 2>&1
커맨드 history에서 찾기
- 아래 예와 같이 history 명령을 이용해서 찾을 수 있다.
$ history | grep "xxx"
- 입력 명령을 사용한 마지막 명령을 재실행하기 (예를 들어 !vi 라고 명령하면 마지막으로 vi를 실행한 명령을 재실행)
$ !{명령}
- Reverse search 이용하기: Shell에서 Ctrl+R을 누른 후, 찾으려는 문자열을 입력한다.
다른 후보를 찾으려면 다시 Ctrl+R을 누르면 되고, 원하는 것이 찾아졌으면 Enter 키를 누르면 선택된다.
직전 경로로 돌아가기
- 아래와 같이 cd 명령을 실행하면 된다.
$ cd -
- 또는 아래와 같이 pushd, popd를 이용할 수도 있다.
$ pushd . $ cd {신규_경로} $ popd
특정 문자열 포함 여부 검사
- 특정 변수가 특정 문자열을 포함하고 있는지는 아래 예와 같이 할 수 있다.
if [[ "$변수" == *"문자열"* ]]; then echo "Found" fi
- 또는 아래 예와 같이 할 수도 있다.
if [[ "$변수" =~ "문자열" ]]; then echo "Found" fi
터미널에서 ANSI color 출력
터미널에서 ANSI color 출력은 ESC key를 이용하면 된다.
ESC key는 \e
, \x1b
(또는 \x1B
), \033
중의 하나를 사용하면 된다. (ESC는 16진수로 0x1B, 8진수로 033 임)
예를 들어 주요 색깔은 아래와 같다.
- 빨간색: \e[31m
- 초록색: \e[32m
- 노란색: \e[33m
- 파란색: \e[34m
- 자홍색: \e[35m
- 청록색: \e[36m
- 속성 off: \e[0m
예를 들어, 아래와 같이 출력할 수 있다.
$ echo -e "\e[31mHello \e[33mthe world! \e[0m"
직전에 실행한 명령어의 리턴값 출력
직전에 실행한 명령어의 리턴값은 아래와 같이 $?
로 확인할 수 있다.
$ echo $?
Bash 스크립트에서 아래 예와 같이 이용할 수 있다.
if [ $? == 0 ]; then
echo "Success"
fi
if [ $? != 0 ]; then
echo "Failed"
fi
if [ $? -ne 0 ]; then
echo "Failed"
fi
직전 명령어 결과로 실행 조건 걸기
- 아래 예와 같이
command1 && command2
하는 경우에는 command1이 성공하면 command2를 실행한다.$ make && echo "Success"
- 아래 예와 같이
command1 || command2
하는 경우에는 command1이 실패하면 command2를 실행한다.$ make || echo "Failed"
- 아래 예와 같이
command1 && command2 || command3
하는 경우에는, command1이 성공하면 command2를 실행하고, 실패하면 command3를 실행한다.$ make && echo "Success" || echo "Failed"
문자열 비교하기
- 문자열이 비어 있는지 (0 바이트인지) 검사하는 예
if [ -z $1 ]; then echo "Empty" else echo "Non empty" fi
- 문자열이 비어 있지 않은지 검사하는 예 (
-n
은 non-empty의 약자)if [ -n $1 ]; then echo "Non empty" else echo "Empty" fi
- 문자열이 같은지 검사하는 예 (변수를 사용하는 경우에는 비어있을 수도 있으므로, “변수명”과 같이 사용하는 것이 좋음)
if [ "$1" = "문자열" ]; then echo "Same" else echo "Different" fi if [ "$1" != "문자열" ]; then echo "Different" else echo "Same" fi
실행 결과를 환경 변수에 저장하기
- 아래 형태로 하면 된다.
변수명=`명령어` 변수명=$(명령어)
- 실행 결과가 (아래 예에서는 df 명령의 실행 결과) 여러 줄인 경우에는 echo 시 따옴표(“)가 없으면 1 줄에 모두 표시되고, 아래와 같이 따옴표(“)를 붙이면 여러 줄로 제대로 표시된다.
RESULT=`df` echo "$RESULT"
- 환경 변수를 자식 프로세스에게도 전달하고 싶으면 아래와 같이 export 시키면 된다.
export {환경 변수명}
현재 절대 경로 얻기
현재 경로의 절대 경로는 아래 방법 등으로 얻을 수 있다.
`pwd`
$(pwd)
`readlink -e .`
입력 파일의 경로 얻기
- dirname 명령: 입력 {경로/파일명}에서 파일 이름을 제외한 경로를 리턴한다.
$ dirname {경로/파일명}
- basename 명령: 입력 {경로/파일명}에서 파일명을 리턴한다.
$ basename {경로/파일명}
참고로 아래와 같이 “-s .확장자” 아규먼트를 추가하면 해당 확장자를 제거한 파일명을 리턴한다.
$ basename -s .확장자 {경로/파일명}
- readlink 명령: 입력 {경로/파일명}의 절대 경로를 얻는다.
$ readlink -e {경로/파일명}
- realpath 명령: 입력 {경로/파일명}의 절대 경로를 얻는다.
$ realpath {경로/파일명}
또, 아래 예와 같이 현재 경로에 대한 입력 {경로/파일명}의 상대 경로를 얻을 수 있다.
$ realpath --relative-to=. {경로/파일명}
- 현재 실행되는 스크립트의 절대 경로는 아래와 같이 얻을 수 있다.
abspath=$(cd ${0%/*} && echo $PWD/${0##*/})
또는
abspath=$(cd ${0%/*} && echo $PWD)
PATH 추가시 중복 방지하기
- PATH를 추가하는 스크립트를 아래 예와 같이 작성하여 실행시키면 중복된 것은 다시 추가되지 않는다.
function addToPath { case ":$PATH:" in *":$1:"*) :;; # already there *) PATH="$1:$PATH";; # or PATH="$PATH:$1" esac } addToPath {추가할_경로}
- 또는 아래 예와 같이 할 수도 있다.
if [[ ! "$PATH" == *{추가할_경로}* ]]; then export PATH=$PATH:{추가할_경로} fi
- 참고로 아래와 같이 실행하면 현재 PATH에서 중복된 PATH가 제거된다.
$ PATH=$(printf "%s" "$PATH" | awk -v RS=':' '!a[$1]++ { if (NR > 1) printf RS; printf $1 }')
파일/경로 존재 여부 검사
- 입력 normal 파일이 존재하는지 검사
$ test -e {파일명}
입력 character device node가 존재하는지 검사
$ test -c {디바이스_노드명}
입력 경로가 존재하는지 검사
$ test -d {경로명}
결과는 아래와 같이 명령의 실행 결과를 보면 된다. (존재하면 0, 존재하지 않으면 1이 출력됨)
$ echo $?
- 아래 예와 같이 응용할 수 있다.
if [ ! -d ${TARGET_DIR} ]; then mkdir ${TARGET_DIR} fi if [ ! -e ${TARGET_FILE} ]; then touch ${TARGET_FILE} fi [ ! -d "$TARGET_DIR" ] && mkdir "$TARGET_DIR"
표준 입력으로 읽기
- 표준 입력으로 읽으려는 경우에는 아래 예와 같이 읽을 수 있다.
$ read {변수명}
- 메시지를 출력하고 사용자 입력을 받으려면 아래 예와 같이
-p
아규먼트를 추가하면 된다.$ read -p "Enter the number: " {변수명}
- 입력시 타임아웃을 주고 싶으면 아래 예와 같이
-t
아규먼트를 추가하면 된다. (아래 예는 3초 타임아웃)$ read -p "Enter the number in 3sec: " -t 3 {변수명}
- Bash 스크립트에서 여러 줄을 읽으려는 경우에는 아래 예와 같이 읽을 수 있다.
while read var do echo "$var" done
Bash 표준 입력 내용으로 실행하기
- 예제 1
$ bash <(curl -s http://test.sh)
- 예제 2
$ curl -s http://test.sh | bash -s
파일을 줄 단위로 읽어 처리하기
- Redirection을 이용하는 예
while read [라인변수명]; do [반복작업할 내용 작성] done < [파일명]
- Pipeline을 이용하는 예
cat [파일명] | while read [라인변수명]; do [반복작업할 내용 작성] done
파일이 2개의 칼럼(space로 분리)을 가지는 경우의 예
cat $file_name | while read dir_name file_name; do echo "base_dir: $dir_name, file_name: $file_name" done
편집기없이 여러 줄의 텍스트 파일 생성하기
- 방법 1: 아래 예와 같이 실행한다.
$ cat > {파일이름} << EOF
실행하면 ‘>’ 프롬프트가 나오는데, 줄 내용을 입력한 후 엔터 키를 누르면 다시 ‘>’ 프롬프트가 나온다.
입력을 끝내고 마치고 싶으면 ‘>’ 프롬프트에서 EOF 문자열을 입력하면 프롬프트에서 빠져 나오고, 입력한 내용으로 파일이 생성된다.참고로 위에서 2군데 EOF 문자열은 원하면 다른 문자열로 바꿔도 무방하다.
- 방법 2: 아래 예와 같이 실행한다.
$ cat > {파일이름}
위 방법 1에 비하여 아무 프롬프트도 표시되지 않는데, 마찬가지로 엔터 키를 눌러가면서 여러 줄을 입력한다.
Ctrl + D 키를 누르면 입력 모드에서 빠져 나오고, 입력한 내용으로 파일이 생성된다. - 방법 3: 아래 예와 같이 echo 명령을 이용할 수도 있다. (신규 줄 입력은
\n
사용)$ echo -e "This is 1st line\nThis is 2nd line\nThis is the last line" > {파일이름}
명령 실행시 사용자 입력을 자동으로 넣기
- 아래 예와 같이 할 수 있다.
$ (echo "6"; echo "69") | sudo apt install -y tcl
- 또는 아래 예와 같이 할 수 있다. (아래에서 EOF는 다른 문자열로도 대체 가능)
$ sudo apt install -y tcl <<EOF 6 69 EOF
- 또는 expect 툴을 설치한 후, 아래 예와 같이 이용할 수 있다.
PW="Password" expect -c " set timeout 5 spawn env LANG=C /usr/bin/ssh ServerName expect \"password:\" send \"${PW}\n\" expect \"$\" exit 0 "
변수 이름으로 변수 값 얻기
아래 예와 같이 eval
과 '$'{변수명}
을 사용하면 된다. (아래 예의 결과로 SUCCESS가 출력됨)
FOO="BAR"
BAR="SUCCESS"
eval echo '$'$FOO
for 문을 이용한 반복 처리 예제
아래 예는 dd 툴을 이용해서 입력 파일에서 필요한 5개의 블럭 데이터를 추출하는 예이다.
INPUT_FILE=input.bin
OFFSET0=$(expr $[0x000000] / $[0x10000]) SIZE0=$(expr $[0x080000] / $[0x10000]) OUT_FILE0=block1.bin
OFFSET1=$(expr $[0x100000] / $[0x10000]) SIZE1=$(expr $[0x120000] / $[0x10000]) OUT_FILE1=block2.bin
OFFSET2=$(expr $[0x220000] / $[0x10000]) SIZE2=$(expr $[0x280000] / $[0x10000]) OUT_FILE2=block3.bin
OFFSET3=$(expr $[0x4A0000] / $[0x10000]) SIZE3=$(expr $[0x080000] / $[0x10000]) OUT_FILE3=block4.bin
OFFSET4=$(expr $[0x220000] / $[0x10000]) SIZE4=$(expr $[0x300000] / $[0x10000]) OUT_FILE4=block5.bin
if [ -f ${INPUT_FILE} ]; then
for ((i = 0; i < 5; ++i)); do
eval OUT_FILE=\$OUT_FILE${i}
eval OFFSET=\$OFFSET${i}
eval SIZE=\$SIZE${i}
dd if=${INPUT_FILE} of=${OUT_FILE} bs=64k skip=${OFFSET} count=${SIZE} 2> /dev/null
done
fi
대소문자 변환하기
- Bash 4 버전 이상부터는 문자열 오른쪽에
^^
키워드를 붙이면 문자열이 모두 대문자로 변환되고, 문자열의 오른쪽에,,
키워드를 붙이면 모두 소문자로 변환된다. (아래 예 참조)str="ApPlE" uppercase=${str^^} lowercase=${str,,} echo "Uppercase: ${uppercase}" echo "Lowercase: ${lowercase}"
- 또는 아래 예와 같이
tr
명령을 이용할 수도 있다.text="Hello, World!" echo ${text} | tr [:upper:] [:lower:] echo ${text} | tr [A-Z] [a-z]
산술 연산하기
- 간단한 연산은 아래 예와 같이 할 수 있다.
echo $((2 + 3)) echo $((2 * 3))
- 또는
expr
명령을 이용하여 아래 예와 같이 할 수 있다. (띄워쓰기 필요함)TEST=`expr 24 - 9` echo $TEST DAY_in_SECONDS=`expr 24 \* 3600` echo $DAY_in_SECONDS
- 또는
bc
명령을 이용하여 아래 예와 같이 할 수 있다.echo "24 - 9" | bc echo "24 * 3600" | bc
10진수를 16진수로 출력은 아래와 같이 할 수 있다.
echo "obase=16; <10진수 숫자>" | bc
- 참고로 expr, bc 명령은 아래 예와 같이 사칙연산, 크기 비교, 문자열 길이 등도 지원한다.
echo "2.20 > 2.21" | bc echo "2.22 > 2.21" | bc