<commit_before>%title Shell
%toc

俺覺得 Shell Script 的主要難點是需要較廣的知識面。
有時會與不會只是對於某個 command 的了解與否。 <br>
(比如: 不了解 `tail` , `head` 等就不能方便的讀取行。
不了解 `pgrep` 不能方便的查找 process。)

大概也就多參考別人寫的，自己多練才會慢慢進步吧(??)。
所以這裡大概記錄些遇到過的知識。

-----

= Resources =
* awesome-shell - github 上別人歸檔的 Shell 資料: https://github.com/alebcay/awesome-shell
* leetcode 中 Shell 分類的題目: https://leetcode.com/problemset/shell/
  - 我當時的解法: http://xiongjia.github.io/dev/2015/06/23/shell-problems/ 

== Tips ==
=== 并行执行 ===
Shell Script 的主要工作基本都是去調用各個 commands 去完成的。<br>
所以如果能讓這些 commands 並行執行是會非常有助於提高效率的。

完成这一個工作需要用到 `wait` command。 <br>
比如下面這個例子: 用 2 條加了 `&` 的 `sleep` commands 表示 2 個任務。
`wait` 沒有參數表示等待 2 個 `sleep` 都結束。`sleep` 只是個例子，
也可以是從網上下載數據或是其他操作。由於並行執行所以應該會快出很多。
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh
sleep 100 &
sleep 200 &
wait
}}}

也可以讓 `wait` 只等待某一個任務結束。 <br>
比如下面這個例子, `$!` 表示第一個 `sleep` 的 process id。加在 `wait` 的參數裏表示只等待
某個 process。這 shell script 應該在 100 秒后退出而，第二個 `sleep` 在 script 退出時還在運行。
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh
sleep 100 &
first_sleep=$!
sleep 1000 &
wait ${first_sleep}
}}}

舉一個實際工作中的簡單例子。copy 比較大的 files。如果正常 copy 是一個完成后再另一個。
可以多個文件同時 copy 最大程度的發揮出 disk 的 I/O 吞吐量。 <br>
比如下面這個例子，從一個列表裏讀出源，執行 `cp`，最後在用 `wait` 等待所有 copy 任務結束:
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh

dest=/your_dest_folder

for file in "bigfile1" "bigfile2" "bigfile3"
do
    cp -v "${file}" "${dest}" &
done
wait
}}}

=== json parser ===
有很多的 configuration file 多用 JSON 格式。在 Shell script 裏有時也需要讀取這些配置的值。<br>
比如下面這個例子，通過 `node` 來的到 .json file 裏面某個字段的值。
如果 JSON 内容是 {"port": 8080 } 則 `PORT` 會賦為 "8080"。
但如果 JSON 内容文件有誤，則 `PORT` 會是 "undefined"。
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh
src_json_file=/your_json_filename

# In this sample, the JSON content = { "port": 8080 }
PORT=`node -pe "JSON.parse(process.argv[1]).port" "$(cat ${src_json_file})"`
echo "Your port is ${PORT}"
}}}

除了 `node` 外，也有其它的 command line json tools, 只是這些一般情況需要安裝。比如:
- jq: https://stedolan.github.io/jq/
- jsawk: https://github.com/micha/jsawk

=== command options ===
實際的 shell script 有時需要從 command options 裏讀取參數，例如: `./test.sh -c config_file -d` 
( `-c` 表示配置文件; `-d` 表示調試方式 )

在 shell script 裏可以用 `getopts` 操作來解析這些 options。比如下面這個例子:
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh

conf_file=/your_default_conf_file
debug_flag=false

while getopts "c:d" opt; do
    case "$opt" in
    c)
        conf_file=${OPTARG}
        ;;
    d)
        debug_flag=true
        ;;
    esac
done

echo "conf_file  = ${conf_file}"
echo "debug_flag = ${debug_flag}"
}}}

`getopts` 的基本用法是 `getopts OPTSTRING VARNAME [ARGS]`。
- `OPTSTRING` - 這個等同于 GNU getopt 裏的用法, 比如 "c:d" 有 ":" 的表示有參數。
  "-c config_file" 中的 config_file 就是參數可以從 `OPTARG` 裏讀取。
- `VARNAME` - 表示 option 的變量名
- `ARGS` - 代表參數列表，默認是 `$@`  (即 shell script 的 command options )
<commit_msg>update shell tips<commit_after>%title Shell
%toc

俺覺得 Shell Script 的主要難點是需要較廣的知識面。
有時會與不會只是對於某個 command 的了解與否。 <br>
(比如: 不了解 `tail` , `head` 等就不能方便的讀取行。
不了解 `pgrep` 不能方便的查找 process。)

大概也就多參考別人寫的，自己多練才會慢慢進步吧(??)。
所以這裡大概記錄些遇到過的知識。

-----

= Resources =
* awesome-shell - github 上別人歸檔的 Shell 資料: https://github.com/alebcay/awesome-shell
* leetcode 中 Shell 分類的題目: https://leetcode.com/problemset/shell/
  - 我當時的解法: http://xiongjia.github.io/dev/2015/06/23/shell-problems/ 
* <The Linux Command Line> - 中、英語版: https://github.com/billie66/TLCL
* <Advanced Bash-Scripting Guide> - http://tldp.org/LDP/abs/html/index.html

== Tips ==
=== 并行执行 ===
Shell Script 的主要工作基本都是去調用各個 commands 去完成的。<br>
所以如果能讓這些 commands 並行執行是會非常有助於提高效率的。

完成这一個工作需要用到 `wait` command。 <br>
比如下面這個例子: 用 2 條加了 `&` 的 `sleep` commands 表示 2 個任務。
`wait` 沒有參數表示等待 2 個 `sleep` 都結束。`sleep` 只是個例子，
也可以是從網上下載數據或是其他操作。由於並行執行所以應該會快出很多。
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh
sleep 100 &
sleep 200 &
wait
}}}

也可以讓 `wait` 只等待某一個任務結束。 <br>
比如下面這個例子, `$!` 表示第一個 `sleep` 的 process id。加在 `wait` 的參數裏表示只等待
某個 process。這 shell script 應該在 100 秒后退出而，第二個 `sleep` 在 script 退出時還在運行。
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh
sleep 100 &
first_sleep=$!
sleep 1000 &
wait ${first_sleep}
}}}

舉一個實際工作中的簡單例子。copy 比較大的 files。如果正常 copy 是一個完成后再另一個。
可以多個文件同時 copy 最大程度的發揮出 disk 的 I/O 吞吐量。 <br>
比如下面這個例子，從一個列表裏讀出源，執行 `cp`，最後在用 `wait` 等待所有 copy 任務結束:
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh

dest=/your_dest_folder

for file in "bigfile1" "bigfile2" "bigfile3"
do
    cp -v "${file}" "${dest}" &
done
wait
}}}

=== json parser ===
有很多的 configuration file 多用 JSON 格式。在 Shell script 裏有時也需要讀取這些配置的值。<br>
比如下面這個例子，通過 `node` 來的到 .json file 裏面某個字段的值。
如果 JSON 内容是 {"port": 8080 } 則 `PORT` 會賦為 "8080"。
但如果 JSON 内容文件有誤，則 `PORT` 會是 "undefined"。
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh
src_json_file=/your_json_filename

# In this sample, the JSON content = { "port": 8080 }
PORT=`node -pe "JSON.parse(process.argv[1]).port" "$(cat ${src_json_file})"`
echo "Your port is ${PORT}"
}}}

除了 `node` 外，也有其它的 command line json tools, 只是這些一般情況需要安裝(非默認安裝)。比如:
- jq: https://stedolan.github.io/jq/
- jsawk: https://github.com/micha/jsawk

=== command options ===
實際的 shell script 有時需要從 command options 裏讀取參數，例如: `./test.sh -c config_file -d` 
( `-c` 表示配置文件; `-d` 表示調試方式 )

在 shell script 裏可以用 `getopts` 操作來解析這些 options。比如下面這個例子:
{{{class="brush: shell; gutter: false; toolbar: false;"
#!/bin/sh

conf_file=/your_default_conf_file
debug_flag=false

while getopts "c:d" opt; do
    case "$opt" in
    c)
        conf_file=${OPTARG}
        ;;
    d)
        debug_flag=true
        ;;
    esac
done

echo "conf_file  = ${conf_file}"
echo "debug_flag = ${debug_flag}"
}}}

`getopts` 的基本用法是 `getopts OPTSTRING VARNAME [ARGS]`。
- `OPTSTRING` - 這個等同于 GNU getopt 裏的用法, 比如 "c:d" 有 ":" 的表示有參數。
  "-c config_file" 中的 config_file 就是參數可以從 `OPTARG` 裏讀取。
- `VARNAME` - 表示 option 的變量名
- `ARGS` - 代表參數列表，默認是 `$@`  (即 shell script 的 command options )

=== strings ===
一些字符串操作。
- 字符串的赋值和引用
{{{class="brush: shell; gutter: false; toolbar: false;"
test_str="abc"
echo "This is test str: ${test_str}"
}}}
- 字符长度计算， `${#var}`
{{{class="brush: shell; gutter: false; toolbar: false;"
test_str="abc"
echo "String lenght is ${#test_str}"
}}}
- 子串引用，基本用法:
{{{class="brush: shell; gutter: false; toolbar: false;"
${var:position:length}
}}}
 * 比如下面 2 个例子，分别是从第 2 个字符到字符结尾; 和从第 2 个字符开始的 3 个字符:
{{{class="brush: shell; gutter: false; toolbar: false;"
test_str="123456789"
echo "Sub string 2 ~ end: ${test_str:2}"
echo "Sub string 2 ~ 5: ${test_str:2:3}"
}}}

{'fraction_non_alphanumeric': 0.16317159038726467, 'fraction_numerical': 0.016378386652380224, 'mean_word_length': 4.637618636755824, 'pattern_counts': {'":': 6, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1743150', 'n_tokens_mistral': 3622, 'n_tokens_neox': 3689, 'n_words': 707}