# [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)