├── LICENSE ├── README.md ├── example └── comsub.log ├── random_pick.py └── scripts ├── 001_calculate_run_time.sh ├── 002_parsing_arguments.sh ├── 003_trap_when_ctrl+c.sh ├── 004_use_variable_from_input.sh ├── 005_hide_user_input_and_wget.sh ├── 006_yes_or_no.sh ├── 007_pick_number.sh ├── 008_change_message_color.sh ├── 009_interactive_interface_in_linux_and_mac.sh ├── 010_progress_bar_using_pv.sh ├── 011_default_value.sh ├── 012_local_variable_in_function.sh ├── 013_check_int_type.sh ├── 014_little quotes_in_little quotes.sh ├── 015_use_variable_and_function_from_external_file.sh ├── 016_extract_tag_from_html.sh ├── 017_count_line_num.sh ├── 018_using_arg_space_in_string.sh ├── 019_error_count_grep_and_command_substitution.sh ├── 020_quit_when_unknown_variable.sh ├── 021_deploy_using_gsutil_rsync_x.sh ├── 022_dirname.sh ├── 023_basename.sh ├── 024_subshell.sh ├── 025_find_and_count_file.sh ├── 026_find_and_grep.sh ├── 027_basename_sed.sh ├── 028_find_files_changed_from_n_days_ago.sh ├── 029_remove_file_not_modified_in_1_year.sh ├── 030_bulk_command(find_xargs).sh ├── 031_add_date_in_filename_when_backup.sh ├── 032_rsync_with_other_folder.sh └── 033_zip_and_send_host.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sung Yun Byeon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## shell-scripts 2 | - Linux bash shell scripts code snippets 3 | 4 | ### Rules 5 | - 매일 2개씩 Shell Script 커밋하기 6 | 7 | ### Random Pick 8 | - 아래 명령어를 입력할 경우 랜덤으로 스크립트 1개 선택 9 | 10 | ``` 11 | python3 random_pick.py 12 | ``` 13 | 14 | ### Reference 15 | - [GoogleShell Style Guide](https://google.github.io/styleguide/shell.xml) 16 | - [Bash 입문자를 위한 핵심 요약 정리 (Shell Script)](https://blog.gaerae.com/2015/01/bash-hello-world.html) 17 | -------------------------------------------------------------------------------- /example/comsub.log: -------------------------------------------------------------------------------- 1 | 2019/07/01 21:10:02 [INFO] 2 | 2019/07/03 22:10:02 [ERROR] File does not exist 3 | 2019/07/04 22:11:02 [ERROR] file does not exist 4 | 2019/07/05 09:11:02 [INFO] script end 5 | -------------------------------------------------------------------------------- /random_pick.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | 4 | def search(dirname, extension='.sh'): 5 | filenames = os.listdir(dirname) 6 | result_lists = [] 7 | for filename in filenames: 8 | ext = os.path.splitext(filename)[-1] 9 | if ext == extension: 10 | result_lists.append(filename) 11 | return result_lists 12 | 13 | 14 | current_folder = os.getcwd() 15 | script_folder = current_folder + '/scripts/' 16 | script_lists = search(script_folder) 17 | 18 | print("문제를 풀어보세요 : ", np.random.choice(script_lists, 1)) 19 | -------------------------------------------------------------------------------- /scripts/001_calculate_run_time.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | START=$(date +%s) 3 | 4 | echo "Calculate run-time" 5 | 6 | # put your script 7 | sleep 3 8 | 9 | END=$(date +%s) 10 | DIFF_SECOND=$(( $END - $START )) 11 | DIFF_MINUTE=$(( $DIFF_SECOND / 60 )) 12 | 13 | echo ${DIFF_SECOND} 14 | -------------------------------------------------------------------------------- /scripts/002_parsing_arguments.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while [ "$1" != "" ]; do 4 | case $1 in 5 | -s ) shift 6 | SERVER=$1 ;; 7 | -d ) shift 8 | DATE=$1 ;; 9 | --parameter|p ) shift 10 | PARAMETER=$1;; 11 | esac 12 | shift 13 | done 14 | 15 | echo ${SERVER} 16 | echo ${DATE} 17 | echo ${PARAMETER} 18 | -------------------------------------------------------------------------------- /scripts/003_trap_when_ctrl+c.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | count=0 4 | trap ' echo 5 | echo "Try count:$count" 6 | exit ' INT 7 | 8 | while : 9 | do 10 | curl -o /dev/null $1 11 | count=$(expr $count + 1) 12 | sleep 1 13 | done 14 | 15 | # while : 띄어쓰기 do를 작성해야 함 16 | # trap은 종료 시그널을 받을 때 로그를 출력하거나 현재 상태를 표시하는 등을 할 때 자주 사용됨 -------------------------------------------------------------------------------- /scripts/004_use_variable_from_input.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -n "Enter your ID: " 4 | 5 | read ID 6 | echo "Now your ID is $ID" 7 | 8 | echo -n "Enter your Name: " 9 | 10 | read NAME 11 | echo "Hello! $NAME" 12 | 13 | echo "Input any key to continue..." 14 | read dummy 15 | 16 | echo "Start" 17 | # 입력값을 얻을 경우 read 사용, Python의 input()과 유사 18 | # 그냥 입력을 대기하고 싶을 경우에도 read 사용 가능 -------------------------------------------------------------------------------- /scripts/005_hide_user_input_and_wget.sh: -------------------------------------------------------------------------------- 1 | #!/bib/bash 2 | 3 | username=guest 4 | hostname=localhost 5 | 6 | echo -n "Password: " 7 | stty -echo 8 | read password 9 | 10 | # stty echo 11 | 12 | echo 13 | wget -q --password="$password" "ftp://${username}@${hostname}/filename.txt" 14 | 15 | # 에코백 : 입력한 문자를 화면에 표시하도록 설정하는 기능 16 | # 에코백 OFF는 stty -echo 17 | # 입력하라고 한 후, 에코백 OFF하고 사용자 input을 받음 18 | # wget -q를 하면 결과나 에러 메세지가 나오지 않음 -------------------------------------------------------------------------------- /scripts/006_yes_or_no.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -n "Would you like continue? [y/n]: " 4 | # 또는 printf 사용 5 | 6 | # 현재 터미널 설정을 tty_state에 백업 7 | tty_state=$(stty -g) 8 | stty raw 9 | 10 | # 키보드에서 문자 하나 읽고 2 이상은 삭제(/dev/null/) 11 | char=$(dd bs=1 count=1 2> /dev/null) 12 | # dd 명령어는 입출력 블록 크기를 1(bs=1)로 설정하고, 입력->출력으로 복사하는 블록 수를 1(count=1)로 해서 입력된 문자를 char에 저장 13 | 14 | # 터미널 설정을 원래대로 돌림 15 | stty "$tty_state" 16 | 17 | echo 18 | 19 | # 입력 문자에 따른 분기 20 | case "$char" in 21 | [yY]) 22 | echo "Input: Yes" 23 | ;; 24 | [nN]) 25 | echo "Input: NO" 26 | ;; 27 | *) 28 | echo "Input: What?" 29 | ;; 30 | esac 31 | 32 | # read 명령어는 반드시 엔터를 쳐야하는 반면, 이 방법은 키를 누르고 바로 처리함 33 | # stty raw 모드는 키 버퍼 처리를 하지 않아 문자마다 처리할 수 있음 34 | # 참고로 esac는 case를 거꾸로 쓴 것 35 | # 조건들은 )로 끝남 36 | # 각 조건 블록은 이중 세미콜론 ;; 37 | -------------------------------------------------------------------------------- /scripts/007_pick_number.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while : 4 | do 5 | echo "Menu :" 6 | echo "1) list file" 7 | echo "2) current directory" 8 | echo "3) exit" 9 | 10 | read number 11 | case $number in 12 | 1) 13 | echo "you pick 1, ls" 14 | ls 15 | ;; 16 | 2) 17 | echo "you pick 2, pwd" 18 | pwd 19 | ;; 20 | 3) 21 | echo "you pick 3, bye~" 22 | exit 23 | ;; 24 | *) 25 | echo "Error, Unknown Command" 26 | ;; 27 | esac 28 | 29 | echo 30 | 31 | done -------------------------------------------------------------------------------- /scripts/008_change_message_color.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Script Start!!" 4 | 5 | # 배경을 회색(47), 문자색을 빨강(31)으로 바꿈 6 | # 이스케이프 시퀀스 작성법 : \033[파라미터m 표시할 문자열 \033[0m 7 | echo -e "\033[47;31m Important Message \033[0m" 8 | echo -e "\033[31m Hi, Red \033[0m" 9 | echo -e "\033[34m Hi! Blue \033[0m" 10 | 11 | DARKGRAY='\033[1;30m' 12 | RED='\033[0;31m' 13 | LIGHTRED='\033[1;31m' 14 | GREEN='\033[0;32m' 15 | YELLOW='\033[1;33m' 16 | BLUE='\033[0;34m' 17 | PURPLE='\033[0;35m' 18 | LIGHTPURPLE='\033[1;35m' 19 | CYAN='\033[0;36m' 20 | WHITE='\033[1;37m' 21 | SET='\033[0m' 22 | 23 | 24 | echo -e "I ${DARKGRAY}love${SET} github." 25 | echo -e "I ${RED}love${SET} github." 26 | echo -e "I ${LIGHTRED}love${SET} github." 27 | echo -e "I ${GREEN}love${SET} github." 28 | echo -e "I ${YELLOW}love${SET} github." 29 | echo -e "I ${BLUE}love${SET} github." 30 | echo -e "I ${PURPLE}love${SET} github." 31 | echo -e "I ${LIGHTPURPLE}love${SET} github." 32 | echo -e "I ${CYAN}love${SET} github." 33 | echo -e "I ${WHITE}love${SET} github." 34 | 35 | 36 | echo "Script End!" 37 | -------------------------------------------------------------------------------- /scripts/009_interactive_interface_in_linux_and_mac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LOG_DIR=/myapp/ap1/log 4 | 5 | # # Linux 6 | # # dialog 명령어로 달력 출력 7 | # # 선택한 날짜는 표준 에러 출력이라 임시 파일에 리다이렉트 8 | # dialog --calendar "Select Date" 2 60 2 >cal.tmp 9 | 10 | # # 달력은 년월일로 바꿈 11 | # date_str=$(awk -F / '{print $3$2$1}' cal.tmp) 12 | 13 | # # 취소되면 임시 파일을 삭제하고 종료 14 | # if [ -z "$date_str" ]; then 15 | # rm -f cal.tmp 16 | # exit 17 | # fi 18 | 19 | # rm -i ${LOG_DIR}/app_log.$date_str 20 | 21 | # rm -f cal.tmp 22 | 23 | # Mac은 dialog가 없고 osascript가 존재 24 | osascript -e 'tell app "iTerm" to display dialog "Hello! Popup" with title "script" buttons {"yes","no"}' 2 >return.tmp 25 | # tell app "앱 이름" : 앱에서 26 | # to display dialog "팝업 내용" : 팝업 내용으로 창을 띄우고 27 | # with title "타이틀 이름" : 타이틀 이름의 제목을 가지고 28 | # buttons {"yes", "no"} : yes와 no 2개 버튼을 가진 것을 만들어라! 29 | # 결과는 return.tmp에 저장해라 30 | 31 | return_value=$(awk -F / '{split($1, arr, ":"); print arr[2]}' return.tmp) 32 | # awk -F : 입력 필드를 나눌 때 $1 $2로 사용할 수 있음 33 | # split($1, arr, ":") : $1 값을 ":"로 split하고 그 결과를 arr에 넣어라 34 | # 맨 뒤에 return.tmp는 이걸 읽어서 35 | 36 | echo "$return_value" 37 | rm -f return.tmp -------------------------------------------------------------------------------- /scripts/010_progress_bar_using_pv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # pv 설치 (Mac OS) 4 | # ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null 5 | # brew install pv 6 | 7 | DATA_DIR=/Users/byeon/Downloads 8 | 9 | cd $DATA_DIR 10 | 11 | tar cf - cars_train.tgz | pv | gzip > archive.tar.gz 12 | 13 | # tar 명령어와 gzip 명령어로 큰 파일을 아카이브할 때 처리 진행 상태를 pv 명령어로 표시 14 | # 현재 처리중인 파일을 표시할 땐 v 옵션, 아카이브 작성은 c 옵션, 파일로 작성은 f 옵션 15 | # cf 뒤에 -는 명령어 출력을 파일이 아닌 표준 출력으로 보내겠다고 지정하는 것 16 | 17 | # pv : Pipe Viewer 18 | # 파이프 처리 중 데이터 흐름을 가시화 19 | # -a : 진행바가 아닌 파이프에 흐르는 데이터 평균 속도 표시 20 | # -b : 바이트 숫자만 표시하고 진행바 표시 안함 21 | # -L : 파이프 전송량 제한 22 | # -q : 조용한 모드 23 | # -s : 파이프에 흐르는 데이터 크기 지정 -------------------------------------------------------------------------------- /scripts/011_default_value.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DATA_DIR=/Users/byeon/Downloads 4 | 5 | cd $DATA_DIR 6 | 7 | cp cars_train.tgz ${TMPDIR:=/tmp} 8 | 9 | # TMPDIR값이 있으면 그 값을, 없으면 /tmp를 사용 10 | # :=는 변수에 값이 설정되어 있는지 확인해서 값을 대입 11 | # ${변수명:=값} : 변수가 설정되어 있지 않거나 비어 있을 때 지정한 값 대입 12 | # 변수를 초기하고 싶을 땐 :(널 명령어) 사용하면 됨, : ${TMPDIR:=/tmp} 13 | # :=는 변수값을 기본값으로 덮어씀 14 | # :- : 변수값 설정 체크하고 값은 덮어쓰지 않음 15 | # :? : 셀 변수를 돌려줌, ${var:?message}는 셸 변수 var가 미정이거나 빈 문자열이면 message 출력하고 스크립트 종료 16 | # :+ : word를 돌려줌, ${var:+word}는 셸 변수 var가 미정이거나 빈 문자열이면 null을 반환 17 | # flg=${MYDIR:+1}는 MYDIR이 빈 문자열이 아니면 flg를 1로 설정 18 | 19 | cd $TMPDIR 20 | 21 | tar xzf cars_train.tgz 22 | 23 | echo "Extract files to $TMPDIR." -------------------------------------------------------------------------------- /scripts/012_local_variable_in_function.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=/var/tmp 4 | 5 | ls_home() 6 | { 7 | # 변수 DIR 을 함수 내부 변수로 정의 8 | local DIR 9 | 10 | DIR=~/$1 11 | echo "directory: $DIR" 12 | ls $DIR 13 | } 14 | 15 | ls_home logdir 16 | 17 | echo "directory: $DIR" 18 | ls $DIR 19 | 20 | # 셸 스크립트는 기본적으로 변수 모두를 전역 변수로 취급함. 함수 안에서 변수를 변경하면 스크립트 전체에 영향을 끼침 21 | -------------------------------------------------------------------------------- /scripts/013_check_int_type.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | # test 명령어로 $1이 0과 같은지 -eq로 확인하고, /dev/null로 리다이렉트해서 버림 5 | # 처리의 종료 스테이터스($?)는 다음과 같음 6 | # 인수가 0과 같으면 0 7 | # 인수가 0이 아니면 1 8 | # 인수가 0과 비교 불가능한 문자열이면 2 9 | # 2보다 작으면 정수로 보고 그대로 계산, 정수가 아니면 에러 종료 10 | 11 | test "$1" -eq 0 2>/dev/null 12 | 13 | if [ $? -lt 2 ]; then 14 | echo "Argument is Integer" 15 | expr 10 + $1 16 | else 17 | echo "Argument is not Integer" 18 | exit 1 19 | fi 20 | 21 | -------------------------------------------------------------------------------- /scripts/014_little quotes_in_little quotes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 작은 따옴표로 둘러싼 문자열 안에서 변수 확장 / 작은따옴표 기호를 사용하고 싶을 경우 4 | 5 | price=100 6 | str='It costs $'$price'? I can'\''t believe it!' 7 | 8 | echo $str 9 | 10 | 11 | 12 | # 큰 따옴표로 둘러싼 문자열은 변수 확장과 명령어 치환을 하기 때문에 $와 ₩는 따옴표 안에서도 변수 확장, 명령어 치환 의미를 그대로 가짐 13 | # 작은 따옴표는 단순한 따옴표로 그대로 출력함 14 | 15 | # 큰 따옴표 안에 큰 따옴표를 쓰려면 \ 기호로 큰 따옴표를 이스케이프함 16 | str2="He said \"Hello!\". I said \"Hello\"." 17 | 18 | echo $str2 19 | 20 | # 큰 따옴표가 많은 문자열이면 매번 처리하는게 힘듬. 그럴 경우 작은 따옴표로 처리하면 됨 21 | 22 | str3='He said "Hello!". I said "Hello".' 23 | 24 | echo $str3 25 | 26 | # 작은 따옴표 안에선 변수 확장이 안됨 27 | # 따라서 $'$price' 처럼 끊어서 변수 확장을 유도함 -------------------------------------------------------------------------------- /scripts/015_use_variable_and_function_from_external_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | # env.sh 파일을 닷 명령어로 읽음 : 마치 소스 파일이 그대로 삽입된 것처럼 파일 내부 명령어가 실행 5 | # bash엔 . 와 source와 동일한 역할을 함 6 | # env.sh엔 WORK_DIR 변수와 nowtime() 함수가 구현되어 있음 7 | 8 | . ./env.sh 9 | 10 | nowtime 11 | cp -i -v large-file.tar.gz "$WORK_DIR" 12 | nowtime 13 | 14 | # 닷 명령어를 사용하면 의존 관계가 생기는데, 팀마다 결정하면 좋을 듯 15 | # 이동이 간단해서 의존 관계를 쉽게 해결할 수 있음 16 | # 대상 파일이 없으면 에러가 발생함. -f로 파일 존재 여부를 확인하면 좋음 17 | # 예시 [ -f /etc/sysconfig/sshd ] && . /etc/sysconfig/sshd -------------------------------------------------------------------------------- /scripts/016_extract_tag_from_html.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | filename="example/myapp.log" 4 | 5 | eval $(sed -n "s/\(.*\)<\/code>/\1/p" "example/command.htm") 6 | 7 | # 태그 부분을 패턴 매치해서 추출 8 | # sed 명령어를 통해 문자열 추출, -n 옵션 : 패턴 스페이스 내용을 출력하지 않음 9 | # 마지막에 p 플래스를 붙여서 일치할 때만 패턴 스페이스를 출력 10 | # -n 옵션 + p 플래그를 사용해 치환이 발생한 줄만 출력할 수 있는 테크닉 11 | # eval로 변수 확장해서 명령어 실행 12 | # eval : 인수에 넘어온 문자열을 변수 확장해서 명령어로 실행. 텍스트 문자열을 명령어로 쓸 수 있어서 편함 13 | 14 | # command.htm 15 | # 16 | # Code List 17 | 18 | # 19 | #

This is a sample code.

20 | # date; ls -l $filename 21 | 22 | # 23 | # -------------------------------------------------------------------------------- /scripts/017_count_line_num.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | today="20190703" 5 | 6 | wc -l ${today}_log 7 | # wc : word count 8 | # wc -l : 라인 수 count 9 | 10 | # Bash일 경우 배열 변수 다룰 때 항상 {}를 써야함 11 | declare -a number=("zero" "one" "two") 12 | echo ${number[1]} 13 | # one 14 | echo $number[1] 15 | # zero[1] -------------------------------------------------------------------------------- /scripts/018_using_arg_space_in_string.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | result="invalid value" 4 | 5 | if [ "$result" = "invalid value" ]; then 6 | echo "ERROR: $result" 1>&2 7 | exit 1 8 | fi 9 | 10 | # 공백을 포함한 문자열 취급이 핵심 11 | # 변수 구분자는 셸 변수 IFS에 정의되어 있는데, 기본값은 공백 기호, 탭, 줄 바꿈으로 되어 있음 12 | # 즉, 공백은 변수 구분자를 뜻함 13 | # 공백이 포함된 문자열이 저장된 변수를 따옴표 처리하지 않으면 쪼개진 것처럼 인식함 14 | # 따옴표 없이 $result라고 하면 if [ invalid value = "invalid value" ]; then 꼴이 되서 인수가 많다는 에러가 뜸 15 | # 공백 문자를 포함하든지 안하든지 따옴표 처리를 하는 것이 좋음 -------------------------------------------------------------------------------- /scripts/019_error_count_grep_and_command_substitution.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | err_count=$(grep -c "ERROR" /Users/byeon/Dropbox/workspace/shell-scripts/example/comsub.log) 4 | # grep -c : 검색 후, Count 5 | # 명령어 치환(command substitution) : 출력 결과를 스크립트에서 셸 변수에 대입하고 싶으면 ₩를 사용함 6 | # err_count=$(grep -c "ERROR" ~/Desktop/$(hostname).log) # 이 방법 추천 7 | # err_count=`grep -c "ERROR" ~/Desktop/\`hostname\`).log` # `` # 그레이브를 이스케이프해야 함 8 | echo "Error counts: $err_count" 9 | 10 | date_str=`date +"%Y%m%d"` 11 | echo $date_str -------------------------------------------------------------------------------- /scripts/020_quit_when_unknown_variable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | # set -u : 스크립트 내부에서 미정의 변수를 참조할 떄 에러가 발생함 5 | # 미정의 변수를 쓰게 될 떄 에러를 표시 : unbound variable 6 | 7 | # set -u의 부작용 8 | # 명령행 인수 $1을 다루기 힘들어짐 9 | 10 | COPY_DIR=/myapp/work 11 | 12 | cp myapp.log $COP_DIR 13 | # COP_DIR라고 실수할 경우 14 | 15 | # ./set-u.sh 1 16 | echo "1st arg: $1" 17 | # 1st arg: 1 18 | echo "2nd arg: $2" 19 | # unbound variable -------------------------------------------------------------------------------- /scripts/021_deploy_using_gsutil_rsync_x.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR=$(dirname "$0") 4 | TARGET_GS_PATH="gs://your_google_storage_bucket_path" 5 | 6 | gsutil -m rsync -x ".*DS_Store|.*__pycache__/.*|.*\.csv$|.*idea/.*|.*env/.*" -p -r ${BASEDIR}/ ${TARGET_GS_PATH} 7 | # DS_Store, pycache 폴더, csv 파일, idea 폴더 등을 제외하고 해당 폴더로 넘김 8 | # gsutil -m : 병렬로 데이터 동기화 (속도 빠름) 9 | # rsync -x : 파이썬 regular expression으로 이거 제외함 10 | # -p : --project로 기본 프로젝트를 사용 11 | # -r : recursive, 폴더 통째로 옮김 -------------------------------------------------------------------------------- /scripts/022_dirname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "$(dirname "$0")" 4 | cd "$(dirname "$0")" 5 | 6 | # dirname "$0" 은 디렉토리 부분만 추출 7 | # dirname /Users/byeon/Dropbox/workspace/shell-scripts/scripts/022_dirname.sh 이런 식으로 하면 8 | # /Users/byeon/Dropbox/workspace/shell-scripts/scripts 만 나옴 9 | # cron 실행할 경우, 실행 사용자의 홈 디렉토리에서 실행되기 때문에 이런 식으로 처리해줘야 정상적으로 작동함 -------------------------------------------------------------------------------- /scripts/023_basename.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | prog=$(basename "$0") 4 | # basename은 dirname과 다르게 파일명만 추출함 5 | # basename /Users/byeon/Dropbox/workspace/shell-scripts/scripts/023_basename.sh 6 | # return : 023_basename.sh 7 | # prog=${0##*/} 이렇게 사용할수도 있음. 셸 파라미터 확장을 이용함 8 | # ${parameter##word} 는 parameter값에서 word에 마지막으로 일치하는 부분을 삭제한 값을 얻을 수 있음 9 | 10 | # $0을 사용하는 것은 시스템 도구에서 자주 사용됨 11 | 12 | # 인수가 하나가 아니면 도움말을 표시하고 종료 13 | # $# : 인수의 수가 들어있음 14 | if [ $# -ne 1 ]; then 15 | echo "Usage: $prog " 1>&2 16 | # 에러메세지라 1>&2로 표준 에러 출력 사용함 17 | exit 1 18 | fi 19 | 20 | echo "Start: $prog" 21 | echo " Input Argument: $1" 22 | echo "Stop: $prog ..." 23 | 24 | 25 | # 도움말 표시나 로그 출력시 파일명을 출력하고 싶은 경우 사용 26 | -------------------------------------------------------------------------------- /scripts/024_subshell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ( 4 | echo "Archive: /var/tmp/archive.tar" 5 | cd /var/tmp 6 | tar cvf archive.tar *.txt 7 | ) 8 | # 괄호 안은 서브셸이 됨 9 | # 서브셸 : 현재 셸 안에서 새롭게 실행되는 셸 10 | # 서브셸 내부에서 일어나는 변경은 호출한 원래 셸에는 영향을 주지 않음 11 | # 유닉스의 부모와 자식처럼, 호출한 곳은 부모 프로세스고 서브셸은 자식 프로세스가 됨 12 | # 서브셸의 변수를 변경해도 원래 셸에선 반영되지 않음 13 | # 서브셸은 "호출한 곳의 환경이 변하지 않는 성질"을 이용해 폴더를 이동하고 다시 움직인 것처럼 진행됨 14 | 15 | echo "Start: command.sh" 16 | ./command.sh 17 | 18 | # 폴더 이동 후, 압축 => 현재 폴더의 command.sh 실행 -------------------------------------------------------------------------------- /scripts/025_find_and_count_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | targetdir="/home/user1/myapp/work" 4 | 5 | filecount=$(find "$targetdir" -maxdepth 1 -type f -print | wc -l) 6 | dircount=$(find "$targetdir" -maxdepth 1 -type d -print | wc -l) 7 | # find [대상 경로] [서술식] 8 | # 서술식은 보통 -name : 특정 이름을 가지는 파일들 찾기 9 | # -type : 파일 종류 지정해서 찾기, -type d(디렉터리), -type f(일반 파일), -type l(심볼릭 링크) 10 | # -maxdepth 1 : 지정 디렉터리만 대상으로 진행, 서브 디렉터리는 포함하지 않음 11 | # -print : 찾은 파일을 그대로 표시 12 | # wc -l : 줄 수 카운트 13 | 14 | dircount=$(expr $dircount - 1) 15 | # 자신을 제외하기 위해 제외 16 | 17 | echo "대상 디렉터리 : $targetdir" 18 | echo "파일 수: $filecount" 19 | echo "디렉터리 수: $dircount" -------------------------------------------------------------------------------- /scripts/026_find_and_grep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | logdir="/var/log/myapp" 4 | 5 | find $logdir -name "*.log" -print | xargs grep "ERROR" /dev/null 6 | 7 | # find 명령어로 파일 목록을 출력해서 xargs로 명령어 받아서 grep 실행함 8 | # 파일에 공백 문자가 포함되면 에러가 발생함. 공백을 다루려면 -print0 옵션을 이용해야 함 -------------------------------------------------------------------------------- /scripts/027_basename_sed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for htmlfile in *.html 4 | do 5 | # 파일명에서 확장자 제외한 문자열 취득 6 | fname=$(basename $htmlfile .html) 7 | 8 | # 태그 내용을 후방참조\1로 추출, 파일 출력 9 | sed -n "s/^.*<title>\(.*\)<\/title>.*$/\1/p" $htmlfile > output/${fname}.txt 10 | done -------------------------------------------------------------------------------- /scripts/028_find_files_changed_from_n_days_ago.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | logdir="/var/log/myapp" 4 | 5 | find $logdir -name "*.log" -mtime -4 mtime +1 -print 6 | # 4일 전에저 2일 전까지 갱신된 로그 파일 목록을 표시함 7 | # 파일 검색시 확장자 지정은 find의 -name에 작성함 8 | # 파일 변경일을 확인할 경우 -mtime를 사용 9 | # -n : n일 전 이후 10 | # n : n+1일 전부터 n일까지 11 | # +n : n일전보다 과거 12 | 13 | 14 | # 수정한지 20일 이상(-mtime +20)된 파일과 디렉토리 출력 15 | find $logdir -mtime +20 -ls 16 | 17 | # 수정한지 20일 이상된 파일만 출력 18 | find $logdir -mtime +20 -type f -ls 19 | 20 | 21 | # 수정한지 20일 이상된 파일만 삭제 ( -exec rm {} \; ) 22 | # (정기적으로 20일이 지난 파일을 삭제할 때 유용) 23 | find $logdir -mtime +20 -type f -ls -exec rm {} \; 24 | 25 | # 수정한지 3일 이내( -mtime -3 )의 파일만 (백업할 때 유용) 26 | find $logdir -mtime -3 -type f -ls 27 | 28 | # 수정한지 30분 이내( -mmin -30 )의 파일만 29 | find $logdir -mmin -30 -type f -ls -------------------------------------------------------------------------------- /scripts/029_remove_file_not_modified_in_1_year.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 오래동안 변경되지 않은 파일이나 오래된 로그 파일을 삭제하고 싶을 경우 사용 4 | 5 | logdir="/var/log/myapp" 6 | 7 | find $logdir -name "*.log" -mtime +364 -print | xargs rm -fv 8 | 9 | # xargs : 파일 목록을 인수로 받아서 다음에 나오는 명령을 던짐 10 | # 파이프로 자주 사용하는 패턴임 11 | # rm에서 -f : 파일이 하나도 없을때 에러가 발생하지 않도록 12 | # -v : 삭제한 파일명 표시 13 | # 처음부터 xargs rm -fv 하지말고 처음엔 xargs ls로 확인한 후, 진행하는 것 추천 -------------------------------------------------------------------------------- /scripts/030_bulk_command(find_xargs).sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | logdir="/var/log/myapp" 4 | 5 | find $logdir -name "*.log" -print | xargs grep "ERROR" /dev/null 6 | # 확장자가 .log 파일에서 ERROR 문자열 검색 7 | # 너무 많을 경우 Argument list too ling 에러가 발생하는데, ARG_MAX 상한선 때문일 수 있음 8 | # 아래 명령어로 상한값 확인 가능 9 | getconf ARG_MAX 10 | # /dev/null 이 있는 이유 : grep 출력에 파일명을 포함하기 위함 11 | # grep "ERROR" * 에서 파일 수에 따라 결과 ㅜㄹ력이 달라지는데, /dev/null을 추가하면 늘 복수 개의 파일을 대상으로 실행되서 파일명이 표시됨 12 | # grep "ERROR" *이 복수일 경우 13 | # <파일명>:<일치한 줄> 14 | # grep "ERROR" *이 단수일 경우 15 | # <일치한 줄> -------------------------------------------------------------------------------- /scripts/031_add_date_in_filename_when_backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 어떤 파일을 백업할 때 현재 날짜를 넣어서 간단히 복사하고 싶은 경우 4 | 5 | config='myapp.conf' 6 | back_filename="${config}.$(date '+%Y%m%d')" 7 | 8 | # 만약 date까지 중복이 있다면 초까지 넣어서 백업 파일 작성 9 | if [ -e $back_filename ]; then 10 | back_filename="${config}.$(date '+%Y%m%d%H%M.%S')" 11 | fi 12 | 13 | cp -v "$config" "$back_filename" 14 | # -v 옵션으로 어떤 파일명으로 복사하는지 표시 -------------------------------------------------------------------------------- /scripts/032_rsync_with_other_folder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | log_dir="/home/user1/myapp/log" 4 | backup_dir="/backup/myapp" 5 | 6 | # log_dir 안에 있는 로그 파일을 backup_dir 디렉토리에 복사 7 | 8 | rsync -av "$log_dir" "$backup_dir" 9 | # rsync는 새롭게 생긴 파일만 차분 10 | # 복사할 때 원본과 대상의 차이를 바탕으로 갱신된 파일만 복사해 효율적 11 | # 파일 타임스탬프, 퍼미션, 소유자 정보 등 파일 속성을 그대로 복사할 수 있음 12 | # ssh를 사용해 원격 서버에서도 복사할 수 있음 13 | # -a : 아카이브 모드, 자주 사용하는 옵션을 하나로 묶은 옵션으로 파일 타임스탬프, 퍼미션, 소유자 정보를 그대로 복사 14 | # -v : verbosed 모드, 상세모드, 복사하는 파일 목록과 전송량을 표시하는 옵션 15 | # -n : dry run 모드, 실제 파일 복사는 하지 않고 처리될 대상 파일 목록만 출력함 16 | 17 | # 원격 서버에서 사용시 "사용자명@호스트명:"을 지정 18 | # rsync -av -e ssh /home/user1/myapp/log user1@server1:/backup/myapp 19 | # 복사 원본에서 지워진 파일을 복사 대상에서도 지우고 싶을 경우 --delete 옵션 사용 -------------------------------------------------------------------------------- /scripts/033_zip_and_send_host.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | username="user1" 4 | server="192.168.1.5" 5 | 6 | tar cvf - mypp/log | ssh ${username}@${server} "cat > /backup/myapplog.tar" 7 | 8 | # myapp/log 디렉토리를 아카이브한 tar 파일을 192.168.1.5 서버의 /backup 디렉토리에 복사 9 | # 매일 또는 매주 정기적으로 실행하는경우가 존재 10 | # 표준 출력에 tar 아카이브를 출력하는 - 옵션 사용 11 | # c(아카이브 작성), v(처리 파일 표시), f(아카이브 파일 사용) --------------------------------------------------------------------------------