# [AIdrifter CS 浮生筆錄](https://hackmd.io/s/rypeUnYSb) : <br> How to debug on bash ## Introduction - batch file - Unix 系列: Xenix(1989 sh) -> SunOS(tcsh) -> RedHat(sh->bash) -> ArchLinux(2005 zsh) - Windows 系列: DOS(command.com) -> Windows(cmd.exe) -> PowerShell ## Error proofing - set -euo pipefail - shellcheck https://www.shellcheck.net/ - vim plugin, https://github.com/vim-syntastic/syntastic 以 :w 檢查, 以 :Erros 列出訊息列表, .vimrc 參考: ```shell Plug 'vim-syntastic/syntastic' set statusline+=%F\ %l\:%c set statusline+=%#warningmsg# set statusline+=%{SyntasticStatuslineFlag()} set statusline+=%* let g:syntastic_always_populate_loc_list = 0 let g:syntastic_auto_loc_list = 0 let g:syntastic_check_on_open = 0 let g:syntastic_check_on_wq = 0 " 可用這些指令 :SyntasticInfo :Errors :lnext :lprev ``` ## Debug skill ### export PS4 in your ~/.bashrc ```shell # not complete... export PS4='+\y $BASH_SOURCE:$LINENO: ${function}' ``` - How to use it ? ```shell aidrifter@aidrifter-VM$ bash -x cdargs-bash.sh +\y cdargs-bash.sh:9: CDARGS_SORT=0 +\y cdargs-bash.sh:10: CDARGS_NODUPS=1 +\y cdargs-bash.sh:149: alias cb=cdb +\y cdargs-bash.sh:150: alias cv=cdb +\y cdargs-bash.sh:258: '[' -n '' ']' +\y cdargs-bash.sh:261: '[' -z '4.4.7(1)-release' ']' +\y cdargs-bash.sh:267: _cdargs_complete +\y cdargs-bash.sh:252: local nospace= +\y cdargs-bash.sh:253: '[' 4 -ge 3 -o '(' 4 = 2 -a '(' 4 = 05a -o 4 = 05b ')' ')' ']' +\y cdargs-bash.sh:253: nospace='-o nospace' +\y cdargs-bash.sh:254: complete -o nospace -S / -X '*/' -F _cdargs_aliases cv cb cdb ``` ### read -rp "Enter" ans ### caller: bash SHELL BUILTIN COMMANDS ## Good Sample ### reflector.sh Source - reflector.sh 是一個將Arch Linux download mirrors依照存取速度排序的工具 - 全篇最重要技巧, 透過 xargs 同時以 curl 測試各網站連線速度 - 提醒: `chmod +x` 可以讓指令直接能被執行,相應的執行程式會被定義在檔案第一行,例如 #!bash - 如何分辨指令是不是 bash 內建的?`command -v bash`這種會給出來源的就不是,`command -v if`這種給不出來源的就是。 - 建議編寫shell script時使用有syntax highlight的編輯器,至少保留字會被換顏色,確保沒有筆誤 ```shell= #!/bin/bash ### https://en.wikipedia.org/wiki/Shebang_(Unix) # 只能代一個參數 # Updated: Tues May 07 21:04:12 2013 by webmaster@askapache # @ https://www.askapache.com/shellscript/reflector-ranking-mirrors/ # Copyright (C) 2013 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # if mirrors exists, cat it, otherwise create it ### 1.function: man bash 再查 Shell Function Definitions ### 2.{{{1: vim 編輯器 Voom 外掛 function get_mirrors () #{{{1 { ### if...else...fi: man bash 再查 Compound Commands (用 else 去查找到的) ### '[[': man bash 再查 Compound Commands (用 \[\[ 找到) ### -s: man bash 再查 CONDITIONAL EXPRESSIONS if [[ -s $MIRRORS ]]; then cat $MIRRORS; else ### curl, sed: man <keyword> ### |,>:: https://en.wikipedia.org/wiki/Pipeline_(Unix) # 查 bash Pipelines curl -LksS -o - 'https://www.archlinux.org/mirrors/status/json/' | \ sed 's,{,\n{,g' | sed -n '/rsync/d; /pct": 1.0/p' | sed 's,^.*"url": "\([^"]\+\)".*,\1,g' > $MIRRORS ### cat: man cat cat $MIRRORS; fi } function get_core_urls () #{{{1 { get_mirrors | sed "s,$,core/os/${ARCH}/core.db.tar.gz,g" } function get_gcc_urls () #{{{1 { get_mirrors | sed "s,$,core/os/${ARCH}/${GCC_URL},g" } # rm tmp file on exit ### trap: which trap, type trap, man which # SHELL BUILTIN COMMANDS trap "exitcode=\$?; (rm -f \$MIRRORS 2>/dev/null;) && exit \$exitcode" 0; trap "exit 1" 1 2 13 15; # file containing mirror urls ### mktemp: 建立暫存檔 # 最早是用 /tmp/$$.myfile ### && ||: 查 '&&', 先 '&&' 再 '||' MIRRORS=`(mktemp -t reflector-mirrorsXXXX) 2>/dev/null` && test -w "$MIRRORS" || MIRRORS=~/reflector.mirrorsXXX # arch ### uname 判斷主機 32 或 64 位元 ARCH=`(uname -m) 2>/dev/null` || ARCH=x86_64 # the gcc file GCC_URL=$( curl -LksSH --url ftp://ftp.archlinux.org/core/os/${ARCH}/ 2>/dev/null | sed -n 's/^.*\ \(gcc-[0-9]\+.*.tar.xz.sig\)\ -.*$/\1/gp' ); ### '{' 和 '(' 不同, 不需要產生 sub-shell { # faster as primarily used to pre-resolve dns for 2nd core test ### 常用小工具 xargs, sort, head, cut, sed, awk get_gcc_urls | xargs -I'{}' -P40 curl -Lks -o /dev/null -m 3 --retry 0 --no-keepalive -w '%{time_total}@%{speed_download}@%{url_effective}\n' --url '{}' |\ sort -t@ -k2 -nr | head -n 50 | cut -d'@' -f3 | sed 's,core/os/'"${ARCH}/${GCC_URL}"',$repo/os/$arch,g' get_core_urls | xargs -I'{}' -P10 curl -Lks -o /dev/null -m 5 --retry 0 --no-keepalive -w '%{time_total}@%{speed_download}@%{url_effective}\n' --url '{}' |\ sort -t@ -k2 -nr | head -n 50 | cut -d'@' -f3 | sed 's,core/os/'"${ARCH}"'/core.db.tar.gz,$repo/os/$arch,g' } | sed 's,^,Server = ,g' | awk '{ if (!h[$0]) { print $0; h[$0]=1 } }' ### 覺得 exit $? 多寫, 最後的 ; 也是多寫的 exit $?; ``` ### wait-for.sh Source - 全篇重點在使用 busybox 支援的 nc 小工具, 偵測 port 是否開放聽取 ```shell= #!/bin/sh ### 用 sh 讓 https://en.wikipedia.org/wiki/BusyBox 也可以跑 ### 好習慣, 常數用全大寫 TIMEOUT=15 QUIET=0 ### 函數用短寫法才會相容於 busybox 的 ash echoerr() { if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi } ### << USAGE: Here Documents ### >&2: 輸出到 stderr usage() { exitcode="$1" cat << USAGE >&2 Usage: $cmdname host:port [-t timeout] [-- command args] -q | --quiet Do not output any status messages -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout -- COMMAND ARGS Execute command with args after the test finishes USAGE exit "$exitcode" } wait_for() { ### * for ... in ...; do ... done * `(backquote): 在bash 建議用 $(...) * seq: 是從 1..$TIMEOUT 小技巧 for i in `seq $TIMEOUT` ; do ### nc nc -z "$HOST" "$PORT" > /dev/null 2>&1 result=$? if [ $result -eq 0 ] ; then if [ $# -gt 0 ] ; then exec "$@" fi exit 0 fi ### sleep (bash 的 sleep 支援 0.1 秒) sleep 1 done echo "Operation timed out" >&2 exit 1 } ### while...do...done ### $#: 命令列參數個數, 'Special Parameters' ### -gt: 數字比較, 查 man test while [ $# -gt 0 ] do ### case ... in ... esac case "$1" in *:* ) HOST=$(printf "%s\n" "$1"| cut -d : -f 1) PORT=$(printf "%s\n" "$1"| cut -d : -f 2) shift 1 ;; -q | --quiet) QUIET=1 shift 1 ;; -t) TIMEOUT="$2" if [ "$TIMEOUT" = "" ]; then break; fi shift 2 ;; --timeout=*) TIMEOUT="${1#*=}" ### shift 這裡的 1 可省略 shift 1 ;; --) shift break ;; --help) usage 0 ;; *) echoerr "Unknown argument: $1" usage 1 ;; esac done if [ "$HOST" = "" -o "$PORT" = "" ]; then echoerr "Error: you need to provide a host and port to test." usage 2 fi wait_for "$@" ``` ## 進階command ### xargs ### sort ### head ### cut ### sed ### awk ## Reference [Bash alternative to Reflector for Ranking Mirrors](https://www.askapache.com/shellscript/reflector-ranking-mirrors/) [看懂源碼基礎 - 批次檔除錯及排版工具 - Daniel Lin 林原志](https://hackmd.io/c/coscup18-source/%2Fcoscup18-source-shell)