# Function Samples ##### 清理目錄裡的 \*.gz 檔案 用法: KEEP=100 CleanUp /path/to/dir ```shell CleanUp() { dest="$1" if [ $dest ];then CheckDIR $dest cd $dest echo "-> Cleaning up the directory $dest [$(date +'%Y-%m-%d %T')] ..." total=$(ls -lt *.gz 2>/dev/null | grep -v "^d" | grep -v "^total" | wc -l) echo " [!] The numbers of the existing files: $total" tail_num=$(( $total - $KEEP )) if [ $tail_num -gt 0 ];then for f in $(ls -lt *.gz | grep -v "^d" | grep -v "^total" | tail -$tail_num | awk -F ' ' '{print $9}') do #echo " [!] Deleted file: $f" rm -vf $f done else echo " [!] No need of deletion" fi fi } ``` ##### 檢查目錄 用法: CheckDIR /path/to/dir ```shell CheckDIR() { echo -n "-> Checking the directory <$1> ...... " if [ ! -d $1 ] then echo "[Failed]" exit 1 else echo "[OK]" fi } ``` ##### ip 驗證 ```shell # Test an IP address for validity: # Usage: # valid_ip IP_ADDRESS # if [[ $? -eq 0 ]]; then echo good; else echo bad; fi # OR # if valid_ip IP_ADDRESS; then echo good; else echo bad; fi # function is_ip() { local ip=$1 local stat=1 if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=($ip) IFS=$OIFS [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] stat=$? fi return $stat } ``` ##### 驗證數值型資料 ```shell Is_number() { # Usage: Is_number ${your-number} 0 # Type: Number with decimal # If the number is invalid, return 0. # If not specify, return NULL local input err_num re input=$1 err_num=$2 re="^[0-9]+([.][0-9]+)?$" if [[ $input =~ $re ]]; then echo $input else echo $err_num fi } log_utilization_percent=$(Is_number $raw 0) # Validate for integer [[ $port =~ ^-?[0-9]+$ ]] && dosomething # No minus character [[ $port =~ ^[0-9]+$ ]] && dosomething ``` ##### 分割線(自動調整寬度) ```shell CL () { WORDS=$@; termwidth="$(tput cols)"; padding="$(printf '%0.1s' ={1..500})"; printf '%*.*s %s %*.*s\n' 0 "$(((termwidth-2-${#WORDS})/2))" "$padding" "$WORDS" 0 "$(((termwidth-1-${#WORDS})/2))" "$padding"; } CL "This is Seperator Line" ``` ##### 大小寫轉換 ```shell ToUpper() {     echo $1 | tr "[:lower:]" "[:upper:]" } ToLower() { echo $1 tr "[:upper:]"} "[:lower:]" } ``` ##### URL 字串編碼 ```shell function urlencode() {     local data     if [[ $# != 1 ]]; then         echo "Error: No string to urlencode"         return 1     fi     data="$(curl -s -o /dev/null -w %{url_effective} --get --data-urlencode "$1" "")"     if [[ $? != 3 ]]; then         echo "Unexpected error" 1>&2     else         echo "${data##/?}"     fi     return 0 } urlencode "$1" function urldecode() { # urldecode local url_encoded="${1//+/ }" printf '%b' "${url_encoded//%/\\x}" } ``` ##### 函數回傳字串 - https://linuxhint.com/return-string-bash-functions/ - https://www.linuxjournal.com/content/return-values-bash-functions Example-1: Using Global Variable ```shell function F1() { retval='I like programming' } retval='I hate programming' echo $retval F1 echo $retval ``` Example-2: Using Function Command ```shell function F2() { local retval='Using BASH Function' echo "$retval" } getval=$(F2) echo $getval ``` Example-3: Using Variable ```shell function F3() { local arg1=$1 if [[ $arg1 != "" ]]; then retval="BASH function with variable" else echo "No Argument" fi } getval1="Bash Function" F3 $getval1 echo $retval getval2=$(F3) echo $getval2 ``` ##### is\_root 用途:檢查是否以 root 帳號執行 ```shell is_root() { current_user="`id`" case "$current_user" in uid=0\(root\)*) ;; *) echo "$0: You must have root privileges in order to install ${product_name}" echo "su as root and try again" echo exit 1 ;; esac } ``` With if-then ```bash if [ ${EUID} -ne 0 ] then exit 1 # this is meant to be run as root fi Run_MyCodes ``` ##### check\_os 用途:檢查 OS 版本 範例一 ```shell check_os() { if [ -f /etc/redhat-release ];then rhel=`grep "^Red Hat Enterprise Linux" /etc/redhat-release` centos=`grep "^CentOS" /etc/redhat-release` release=`cat /etc/redhat-release | cut -d'.' -f1 | awk '{print $NF}'` if [ ! -z "${rhel}" ];then sys="rhel" fi if [ ! -z "${centos}" ];then sys="rhel" fi fi if [ -z "${sys}" -o -z "${release}" ];then echo "Can not determine Linux distribution." exit 127 fi if [ -f /proc/vz/veinfo ];then # we are under Virtuozzo virtuozzo=1 else virtuozzo=0 fi if [ "${VNDEBUG}" ];then debugrpm='--rpmverbosity=debug' else debugrpm='' fi } ``` 範例二 ```shell # Detect the platform for different environment. OS="$(uname)" case $OS in "Linux") MAIL_CMD="/bin/mail" ;; "AIX") MAIL_CMD="/usr/bin/mail" ;; *) MAIL_CMD="/bin/mail" ;; esac ``` 範例三 ```bash get_linux_distribution () { if [ -f /etc/debian_version ]; then DIST="DEBIAN" HTTP_DIR="/etc/apache2/" HTTP_CONFIG=${HTTP_DIR}"apache2.conf" MYSQL_CONFIG="/etc/mysql/mariadb.conf.d/50-server.cnf" elif [ -f /etc/redhat-release ]; then DIST="CENTOS" HTTP_DIR="/etc/httpd/" HTTP_CONFIG=${HTTP_DIR}"conf/httpd.conf" MYSQL_CONFIG="/etc/my.cnf" else DIST="OTHER" echo 'Installation does not support your distribution' exit 1 fi } ``` ##### IsRunning 用途:檢查自身程序是否已經執行中,如果有,程序立即跳離,可避免同一個 script 重複被執行。 ```shell IsRunning() { if [ ! -e $script_pid ];then RUNCOUNT=0 else RUNPID=$(cat $script_pid) RUNCOUNT=$(ps -fp $RUNPID | grep -c `basename $0`) fi #echo $RUNCOUNT if [ $RUNCOUNT -eq 0 ];then echo $$ > $script_pid return 1 # return False else return 0 # return True fi } script_pid="/tmp/$(basename $0).$(whoami).pid" # Check if the program is already running. if IsRunning then echo "Abort: The program is already running!" exit 1 fi ``` ##### getopts 指令參數 ```shell function usage () { cat <<- EOT Usage : ${0##/*/} [options] -t application -q 1024,1055,1077 -m dpggen -u kroybal -r ods [--] Options: -d|debug Bash Debugging Info -m|machine Database host machine -t|table Starting table -u|user User to connect to DB -q|quals Qualifying records to delete -r|relation Database name -s|schema Logical Partition -h|help Display this message -v|version Display script version EOT } # ---------- end of function usage ---------- #----------------------------------------------------------------------- # Handle command line arguments #----------------------------------------------------------------------- [[ $# -eq 0 ]] && { # no arguments usage exit 1 } while getopts ":dhm:q:r:s:t:u:v" opt do case $opt in d|debug ) set -x;; h|help ) usage; exit 0 ;; t|table ) table=$OPTARG;; q|quals ) quals=$OPTARG;; m|machine ) host=$OPTARG;; u|user ) user=$OPTARG;; r|relation ) db=$OPTARG;; s|schema ) schema=$OPTARG;; v|version ) echo "$0 -- Version $ScriptVersion"; exit 0 ;; \? ) echo -e "\n Option does not exist : $OPTARG\n" usage; exit 1 ;; esac # --- end of case --- done shift $(($OPTIND-1)) ``` ```shell Usage() { echo "Usage: $0 [-t ] [-n ]"; echo "For example: $0 -t \"Hello, SuperMan\" -n \"085713 457199\" " } while getopts "t:n:" o; do case "$o" in t) t=$OPTARG ;; n) n=$OPTARG ;; \?) echo "Invalid option: -$OPTARG" Usage exit 1 ;; :) echo "Option -$OPTARG requires an argument !" Usage exit 1 ;; esac done if [ $OPTIND -ne 5 ]; then echo "Invalid options entered !" Usage exit 1 fi ``` 常用參數代號 ``` [Option] [Typical meaning] -a All, append -b Buffer,block size, batch -c Command, check -d Debug, delete, directory -D Define -e Execute, edit -f File, force -h Headers, help -i Initialize -I Include -k Keep, kill -l List, long, load -m Message -n Number, not -o Output -p Port, protocol -q Quiet -r Recurse, reverse -s Silent, subject -t Tag -u User -v Verbose -V Version -w Width, warning -x Enable debugging, extract -y Yes -z Enable compression ``` Without getopts ```shell while [[ "${1}" != "" ]]; do case "${1}" in --vertical) verticle="true" ;; --add-txt) add_txt="true" ;; --replace) replace="true" ;; --verify) verify="true" ;; esac shift 1 done ``` ##### Pause 暫停 用法: pause > 可用於程式開發及偵錯 ```shell pause() { local force_pause=$1 if [ -z $force_pause ]; then if test $NONINTERACTIVE; then return 0 fi else shift fi [ $# -ne 0 ] && echo -e $* >&2 echo -e "Press [Enter] to continue...\c " >&2 read tmp return 0 } ``` ##### Log 訊息 ```shell log() { # classic logger local prefix="[$(date +%Y/%m/%d\ %H:%M:%S)]: " echo "${prefix} $@" >&2 } log "INFO" "a message" ``` ##### Error 錯誤訊息 用途:輸出錯誤訊息 ```shell error() { echo -e "Error: $*!" >&2 return 0 } ``` ```shell die() { echo echo "ERROR: $*" echo echo "Check the error reason, fix and try again." echo "You are welcome to open a support ticket at https://help.4psa.com!" echo rm -f ${repolocal} local$$ rm -f /var/lock/subsys/vninstaller exit 1 } die() { printf "ERR: %s\n" "$1" >&2 exit 1 } [ -e "$SRC" ] || die "Wal colors not found, exiting script. Have you executed Wal before?" check_deps() { which yum >/dev/null 2>&1 if [ $? -gt 0 ];then echo "yum binary can not be found." die "It's not in the system PATH or in a standard location." fi which ifconfig >/dev/null 2>&1 if [ $? -gt 0 ];then yum -y install net-tools >/dev/null 2>&1 if [ $? -gt 0 ];then echo "ifconfig binary can not be found and package net-tools could not be installed." die "It's not in the system PATH or in a standard location." fi fi } ``` ##### 詢問 Yes/No 用法: getyn "Would you like to install WANPIPE now? \[y\]" > $NONINTERACTIVE 如果程式執行時,不想讓 user 做任何的輸入時,可以為 1 > NONINTERACTIVE=1 ,或者 = 空白,以顯示相關的提示訊息 ```shell prompt() { if test $NONINTERACTIVE; then return 0 fi echo -ne "$*" >&2 read CMD rest return 0 } getyn() { if test $NONINTERACTIVE; then return 0 fi while prompt "$* (y/n) " do case $CMD in [yY]) return 0 ;; [nN]) return 1 ;; *) echo -e "\nPlease answer y or n" >&2 ;; esac done } ``` ##### 字串加密 (hash/encryption) - Hashing, Encryption, Encoding 之間的差異與應用場景: [hashing\_encryption\_encoding.jpg](https://osslab.tw/attachments/86) ```shell _Decrypt() { # Encrypt the string by following the command. # echo "your password" | openssl enc -base64 # NOTE: The openssl requires to be installed. # # Decrypt the hash code by following the command. # _Decrypt "your-hash-key" # local hash hash="$1" if which openssl >/dev/null 2>&1;then if [ -n $hash ];then echo -n "$hash" | openssl enc -base64 -d fi fi return 0 } ``` ```bash # Encrypt echo -n "123456" | openssl enc -aes-256-cbc -a -pass pass:ThisIsPassword U2FsdGVkX188Af3yumHOyI19WPbltgRQnPDACmtRniQ= # Decrypt echo -n "U2FsdGVkX188Af3yumHOyI19WPbltgRQnPDACmtRniQ=" | openssl enc -aes-256-cbc -a -pass pass:ThisIsPassword -d 123456 ``` 帳密資訊雜湊處裡 (hash + salt) ```bash #!/bin/bash ## Create: salt=12345_ protocol=sha1sum read -p "Enter login: " username read -p "Password: " -s pass1 read -p "Repeat: " -s pass2 if [ "$pass1" != "$pass2" ]; then echo "Pass missmatch"; exit 1; else password=$pass1; fi echo -en "$username " >> ./mypasswd echo -e "${salt}${password}" | $protocol | awk '{print $1}' >> ./mypasswd ``` ```bash #!/bin/bash ## Read: salt=12345_ #(samesalt) protocol=sha1sum read -p "Enter username: " username read -p "Enter password: " -s password if [ `grep $username ./mypasswd | awk '{print $2}'` != `echo "${salt}${password}" | $protocol | awk '{print $1}'` ]; then echo -e "wrong username or password"; exit 127; else echo -e "login successfull"; fi ``` ```bash # With openssl echo 'mypassword' | openssl passwd -stdin echo 'mypassword' | openssl passwd -salt "sAlT" -stdin # Toe see mode details openssl passwd -h ``` ##### 批次轉檔 ```shell # set q here q="" helper(){ local p="$*" for f in $p do echo "Working on $f" ffmpeg -nostdin -vaapi_device /dev/dri/renderD128 -i "$f" -vf format=nv12,hwupload -c:v hevc_vaapi -f mkv -rc_mode 1 -qp "$q" "${f%.*}.HEVC.mkv" done } helper '/tmp/r/*.mp4' helper '/tmp/r/*.avi' helper '/path/to/*.mkv' ``` ##### 亂數密碼 ```bash genpasswd() { length=$1 [ "$length" == "" ] && length=16 tr -dc A-Za-z0-9_ < /dev/urandom | head -c ${length} | xargs } password=$(genpasswd) if [ -e "/root/passwordMysql.log" ] && [ ! -z "/root/passwordMysql.log" ] then password=$(awk '{print $1}' /root/passwordMysql.log) fi touch /root/passwordMysql.log echo "$password" > /root/passwordMysql.log ``` ##### 對話框 `whiptail` 訊息視窗 ```bash whiptail --title "Example Dialog" --msgbox "This is an example of a message box. You must hit OK to continue." 8 78 ``` Yes/No ```bash if (whiptail --title "Example Dialog" --yesno "This is an example of a yes/no box." 8 78); then echo "User selected Yes, exit status was $?." else echo "User selected No, exit status was $?." fi ``` 輸入視窗 ```bash COLOR=$(whiptail --inputbox "What is your favorite Color?" 8 39 Blue --title "Example Dialog" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus = 0 ]; then echo "User selected Ok and entered " $COLOR else echo "User selected Cancel." fi echo "(Exit status was $exitstatus)" ``` A file ```bash echo "Welcome to Bash $BASH_VERSION" > test_textbox # filename height width whiptail --textbox test_textbox 12 80 ``` 密碼遮蔽 ```bash PASSWORD=$(whiptail --passwordbox "please enter your secret password" 8 78 --title "password dialog" 3>&1 1>&2 2>&3) exitstatus=$? if [ $exitstatus == 0 ]; then echo "User selected Ok and entered " $PASSWORD else echo "User selected Cancel." fi echo "(Exit status was $exitstatus)" ``` 選單 ```bash whiptail --title "Menu example" --menu "Choose an option" 25 78 16 \ "<-- Back" "Return to the main menu." \ "Add User" "Add a user to the system." \ "Modify User" "Modify an existing user." \ "List Users" "List all users on the system." \ "Add Group" "Add a user group to the system." \ "Modify Group" "Modify a group and its list of members." \ "List Groups" "List all groups on the system." ``` 複選清單 ```bash whiptail --title "Check list example" --checklist \ "Choose user's permissions" 20 78 4 \ "NET_OUTBOUND" "Allow connections to other hosts" ON \ "NET_INBOUND" "Allow connections from other hosts" OFF \ "LOCAL_MOUNT" "Allow mounting of local devices" OFF \ "REMOTE_MOUNT" "Allow mounting of remote devices" OFF ``` 單選清單 ```bash whiptail --title "Radio list example" --radiolist \ "Choose user's permissions" 20 78 4 \ "NET_OUTBOUND" "Allow connections to other hosts" ON \ "NET_INBOUND" "Allow connections from other hosts" OFF \ "LOCAL_MOUNT" "Allow mounting of local devices" OFF \ "REMOTE_MOUNT" "Allow mounting of remote devices" OFF ``` 進度條 ```bash #!/bin/bash { for ((i = 0 ; i <= 100 ; i+=5)); do sleep 0.1 echo $i done } | whiptail --gauge "Please wait while we are sleeping..." 6 50 0 ``` ##### Learning - [How to write a function in bash](https://www.xmodulo.com/write-bash-function.html)