---
tags: 主機管理
---
# Linux Shell Script筆記
:::info
* [Cheatsheet for Linux【Devhints.io】](https://devhints.io/linux)
* [Command line stuff【Devhints.io】](https://devhints.io/command_line)
* [Bash scripting cheatsheet【Devhints.io】](https://devhints.io/bash)
* [Vim cheatsheet【Devhints.io】](https://devhints.io/vim)
* [Cron cheatsheet【Devhints.io】](https://devhints.io/cron)
* [Curl cheatsheet【Devhints.io】](https://devhints.io/curl)
* [Grep cheatsheet【Devhints.io】](https://devhints.io/grep)
* [Sed cheatsheet【Devhints.io】](https://devhints.io/sed)
* [Scp cheatsheet【Devhints.io】](https://devhints.io/scp)
* [Composer cheatsheet【Devhints.io】](https://devhints.io/composer)
* [Vim 編輯器 入門指南 (上) : 用思維的速度寫程式【Gamma Ray 軟體工作室】](https://youtu.be/Yk4s-WLjxug)
* [Vim 編輯器 入門指南 (下) : 用程式來寫程式【Gamma Ray 軟體工作室】](https://youtu.be/iviwEUOLUXE)
* [即將失傳的古老技藝 Vim【高見龍】](https://youtube.com/playlist?list=PLBd8JGCAcUAH56L2CYF7SmWJYKwHQYUDI)
* [Linux Bash Scripting【StudyLab】](https://youtube.com/playlist?list=PLUGKwbLbnkj7wZl4HyXQwF3VHlipLS1Gd)
* [Bash Scripting Tutorials【Putorius Linux Tutorials】](https://youtube.com/playlist?list=PL29tgMqrJrhigOOZFKLMZ8i0u30x_s7lG)
* [挨踢實驗室 / IT LAB](https://www.youtube.com/channel/UCmreSJkj5C2L3BpJsZ7ikvQ/featured)
* [Speedtest For Linux](https://www.speedtest.net/zh-Hant/apps/cli)
* [2013 iT 邦幫忙鐵人賽 - 快快樂樂學會讓電腦幫我做事系列](https://ithelp.ithome.com.tw/users/20005357/ironman/630)
* [2017 iT 邦幫忙鐵人賽 - 30 天的 ShellScript 教室解決開發者的困擾系列](https://ithelp.ithome.com.tw/users/20103656/ironman/1132)
* [簡明 Linux Shell Script 入門教學](https://blog.techbridge.cc/2019/11/15/linux-shell-script-tutorial/)
* [臥龍小三 - Shell 設計入門](http://tech.ols3.net/techdoc/old/shell/book1.html)
* [臥龍小三 - Bash 程式設計網路管理應用 awk 篇](http://tech.ols3.net/techdoc/old/awk_intro/)
* [Shell Scripting Tutorial](https://www.shellscript.sh/)
* [The GNU Awk User’s Guide](https://www.gnu.org/software/gawk/manual/html_node/#SEC_Contents)
* [Guide to the AWK Programming Language](https://www.baeldung.com/linux/awk-guide)
* [Difference Between grep, sed, and awk](https://www.baeldung.com/linux/grep-sed-awk-differences)
* [Using sed to Replace a Multi-Line String](https://www.baeldung.com/linux/sed-replace-multi-line-string)
* [Process Multiple Input Files Using Awk](https://www.baeldung.com/linux/awk-multiple-input-files)
:::
## 何謂Shell
* 使用者登入後,LINUX系統會==建立一個操作的環境==,是==介於使用者和LINUX核心之間的溝通介面==。When you log on, the LINUX system starts running a program that acts as **an interface between you and the LINUX kernel**.
* the primary purpose of the shell is to **interpret your commands**
* it is also known as the LINUX **command interpreter**
* a shell is an **interpreted program**
* 解釋使用者所下達的指令。
* 指令直譯器。
:::info
Linux 的 Bash Shell 命令提示字串可以透過 **PS1** 這個環境變數來設定,通常他都是寫在 ~/.bashrc 或是 ~/.bash_profile 這些 Bash 的設定檔中。
* [自訂 Linux 的 Bash Shell 命令提示字串 Prompt(一):基本用法](https://blog.gtwang.org/linux/how-to-make-a-fancy-and-useful-bash-prompt-in-linux-1/)
* [自訂 Linux 的 Bash Shell 命令提示字串 Prompt(二):進階格式](https://blog.gtwang.org/linux/how-to-make-a-fancy-and-useful-bash-prompt-in-linux-2/)
:::
## 指令種類
* 可分為兩種,分別為「**內部(internal)指令**」與「**外部(external)指令**」。
A shell command can be <u>*internal (built-in)*</u> or <u>*external*</u>. The code execute an internal command is **part of the shell process**, but the code to process an external command **resides in a file** in the form of <u>*a binary executable program file*</u> or <u>*a shell script*</u>.
* 內部指令:shell執行程序的一部分。
* 外部指令:以獨立檔案形式存在 (二進制執行檔或可執行的指令稿)。
* 如何區分:
* `type [指令]`
* `whereis [指令]`
* 指令簡易說明的查詢方式
* 內部指令: `help [指令]`
* 外部指令: `[指令] --help`
* 內部指令,以 `cd` 為例

* 外部指令,以 `mv` 為例

### 內部指令
* 指內建在 shell 中的指令
* 當你執行這類的指令時,系統並**不會因為要執行這個指令而產生額外的行程(process)**
* **執行效率非常高**
### 外部指令
* 指那些不是內建於 shell 中的指令
* 通常是一個二進位的執行檔或是一個可執行的指令稿(script)
* 執行這類的指令時,系統就**會產生而外的行程(process)**
* 執行效率也就會比較低一些
* 外部指令要能跨存放位置執行,依靠==PATH環境變數==的定義
The names of the directories that a shell **searches to find the file corresponding to an external command** are stored in the shell variable ==PATH==. Directory names are **separated by colons** in the Bash shell.
* PATH環境變數定義著外部指令所存置的目錄清單。
* 這個**目錄清單**,通常被稱為shell的**search path**。
* 目錄清單以冒號(:)區隔。
## 指令格式
* 通常分為三個部分
:::info
The shell interprets your command by assuming that ==the first word in a command line== is the name of the **command** that you want to execute. It assumes that any of the remaining words ==starting with a hyphen (-)== are **options** and that ==the rest of them== are the command **arguments**.
:::
```
command options arguments
指令 參數 敘述
ping -c2 168.95.1.1
```
* COMMAND
* OPTIONS
* Long options: --now, --add
* Short options: -n, -a
* ARGUMENTS
## 執行程序 (Process)
### 啟動程序
* 當LINUX系統一開機時,kernel會用程序的型態先啟動一些自己的工作,並啟動一個叫做init的程式。
* 早期常見SysV init、BSD init以及Linux Standard Base (LSB)的init。
* RHEL 6之前採用SysV int,RHEL 7之後則改採 Systemd int。
* 當今LINUX發行版本大多採用Systemd init。
* 可使用以下指令得知所使用的作業系統採用何種init機制。
* `stat /sbin/init`
* `cat /proc/1/comm`
* `ps -p 1`
* init會執行一系列存置於 /etc 目錄下的shell script,稱之為「init script」,這些會啟動所有的系統服務。
* 每個程序都會被賦予一個 process ID (PID),PID的值會以升序(asending order)的方式去分配,但 init 永遠都會是PID 1。
### 建立子程序
* The shell executes commands by **creating child processes** using the **fork** and **exec system calls**.
* Every LINUX process has several attributes, including process ID (**PID**), process ID of parent (**PPID**), **process name**, **process state** (running, suspended, swapped, zombie, etc.), the terminal the process was executed on, the length of time the process has run, and process priority.
* The **ps** command can be used to *display these attributes*.
* The **pstree** command can be used to *display process hierarchies*.
### 背景執行
* 有的程序會在背景執行,不會直接與使用者互動。
* 背景執行的程序有兩個來源:使用者手動拋到背景處理的任務、系統服務(services 或 daemons)。
* 系統服務(services 或 daemons)的名稱通常都會以字母d做結尾,如:httpd、sshd或是ftpd。
* 要把程序放到背景執行,只要在指令後面多一個 & 符號。
* 透過 job 指令可以看到背景執行的指令。
* 透過 fg 指令可以把背景執行的程序送回前景執行。
* 透過 kill 指令可以把背景執行的程序強制終止掉。
## 標準輸入輸出
* 每個執行程序都有一個輸入、兩個輸出
* (0) 基本輸入: stdin
* (1) 基本輸出: stdout
* (2) 基本輸出錯誤: stderr
:::info
[Linux I/O 輸入與輸出重新導向,基礎概念教學](https://blog.gtwang.org/linux/linux-io-input-output-redirection-operators/)
:::
* 管線指令(|): 把一個命令的輸出傳遞給另一個命令當作輸入。
```bash
# 把 command1 的結果當成 command2 的輸入
# 再把 command2 的結果給 command3 當輸入
# …以此類推
Command1 | command2 | command3 | ….
```
* ssh的log
```bash=
# ubuntu
tail /var/log/auth.log
tail -f -n 50 /var/log/secure | grep sshd
cat secure | grep -v 'expired password' | grep -v 'will expire' | grep 'Accepted password' | grep -v '140.130.8.157' | more
cat secure | grep -v 'expired password' | grep -v 'will expire' | grep 'Failed password' | grep -v '140.130.8.157' | more
```
* 輸出結果比較
:::info

:::
## 執行狀態回覆 (Exit Status Code)
* All LINUX commands return an **exit status** of ==0 upon success== and ==nonzero upon failure==. The return status value of a command is stored in the **read-only environment variable `$?`** and can be checked by the calling process.
* 供使用者或執行程序了解上一個指令的執行狀態。
* 大部分的指令或動作都會回覆執行狀態。
* 使用 `echo $?` 的方式提取上一個指令的執行狀態碼。
* 0代表指令執行成功,非0代表指令執行有狀況。

:::info
On POSIX systems the standard exit code is 0 for success and any number from 1 to 255 for anything else.
* [Linux and Unix exit code tutorial with examples](https://shapeshed.com/unix-exit-codes/)
:::
## Shell Script
* **consist of shell commands** to be executed by a shell and is stored in an ordinary LINUX file.
* A **positional parameter** is an argument specified on the command line, used to launch the current process in a shell. Positional parameter values are stored in a special set of variables maintained by the shell.

* IFS 是 Internal Field Separator 的縮寫,是 bash 的特殊變數,用在做字串分割 (splitting) 時做為切割字元 (delimiter),預設值是 space, tab 與 newline 。
```bash=
#!/bin/bash
echo '所有的argument:'
echo '$@ - '$@
# Expands to the positional parameters, starting from one. When the
# expansion occurs within double quotes, each parameter expands to
# a separate word. That is, "$@" is equivalent to "$1" "$2" ...
# If the double-quoted expansion occurs within a word, the expansion
# of the first parameter is joined with the beginning part of the
# original word, and the expansion of the last parameter is joined
# with the last part of the original word. When there are no positional
# parameters, "$@" and $@ expand to nothing (i.e., they are removed).
echo '列出$@內容:'
for i in "$@"; do
echo "$i"
done
echo '$* - '$*
# Expands to the positional parameters, starting from one. When
# the expansion occurs within double quotes, it expands to a single
# word with the value of each parameter separated by the first
# character of the IFS special variable. That is, "$*" is equivalent
# to "$1c$2c...", where c is the first character of the value
# of the IFS variable. If IFS is unset, the parameters are separated
# by spaces. If IFS is null, the parameters are joined without
# intervening separators.
echo '列出$*內容:'
for i in "$*"; do
echo "$i"
done
echo 'argument的個數:'
echo '$# - '$#
echo '父程序的PID:'
echo '$PPID - '$PPID
echo '此程序的PID:'
echo '$$ - '$$
echo '解析指令的位置變數:'
echo '$0 - '$0
echo '$1 - '$1
echo '$2 - '$2
echo '$3 - '$3
echo '$4 - '$4
echo '$5 - '$5
echo '$6 - '$6
echo '$7 - '$7
echo '$8 - '$8
echo '$9 - '$9
echo '此程序執行狀態碼:'
echo '$? - '$?
```

## 各種指令介紹
### test指令
* 檔案目錄存在與屬性檢查
> [Shell Script 測試檔案是否可讀或可寫入](https://www.ltsplus.com/linux/shell-script-check-file-readable-writeable)
* `-e`:檢查檔案是否在在
* `-f`:檢查檔案是否存在 (且為正規檔案 regular file)

* `-d`:檢查目錄是否存在
* `-s`:檢查檔案是否存在 (且為非空文件 not empty)
* `-r`:檢查檔案的讀取性
* `-w`:檢查檔案的可寫性
* `-x`:檢查檔案的可執行性
* 檔案時間與關連比較
* `-nt`:檢查 檔案A 是否比 檔案B 新
* `-ot`:檢查 檔案A 是否比 檔案B 舊
* `-ef`:檢查 檔案A 是否與 檔案B 有鏈結關係
* 空值檢查
* `-z`:檢查值是否為空值
* `-n`:檢查值是否非為空值
* 字元比較
* `==`:檢查字串是否相等
* `!=`:檢查字串是否不相等
* `<`:檢查是否小於某字串
* `>`:檢查是否大於某字串
* 數字比較
* `-eq`:檢查是否等於某值
* `-ne`:檢查是否不等於某值
* `-lt`:檢查是否小於某值
* `-gt`:檢查是否大於某值
* `-le`:檢查是否小於等於某值
* `-ge`:檢查是否大於等於某值
* 指令呈現方式
* 正規寫法: `test expression`
```bash
#!/bin/bash
num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
echo '相等'
else
echo '不相等'
fi
```
* 簡單寫法: `[ expression ]`
```bash
#!/bin/bash
num1="ru1noob"
num2="runoob"
if [ $num1 = $num2 ]
then
echo '相等'
else
echo '不相等'
fi
```
### 系統設定指令簡介
* [ip 指令 — 取代 ifconfig 的工具](https://www.ltsplus.com/linux/ip-command)
* hostnamectl 允許長達 64 個字元的靜態和暫時性主機名稱,並且僅能包含 a-z、A-Z、0-9、-,以及 .。[參考資訊](https://blog.gtwang.org/linux/redhat-centos-7-change-hostname-tutorial/)
* 利用 source 或小數點 (.) 都可以將設定檔的內容讀進來目前的 shell 環境中。[參考資訊](https://lofairy.blogspot.com/2015/10/linux-source.html)
### Linux指令三劍客
* awk指令:
1. `awk 'pattern' filename`
2. `awk '{action}' filename`
3. `awk 'pattern {action}' filename`
4. `awk 'BEGIN{action}{action}END{action}' filename'`
* sed指令: `sed [option] '[address]command' filename`
* grep指令: `grep [option] pattern filename`
* 通常是Linux指令三劍客之一,與其他指令相互搭配
* 案例:看線上使用者
```bash=
who | awk '{print $1 "\t" $2}'
```
* tee指令:將輸出的副本送到標準輸出,另一副本拷貝到相應文件中。
```bash=
head -n 5 /etc/passwd | tee a.out
```
* grep 指令
* Option
* -c 顯示出現次數
* -l 顯示文件名稱
* -n 顯示行號
* -i
* -v
* 正則表達式
```bash=
# ^ 以什麼字元為開頭的
cat /etc/passwd | grep ^root
# $ 以什麼字元為結尾的 (本案例:Linux系統帳號)
cat /etc/passwd | grep nologin$
# ^$ 找空白行
cat /etc/sshd_config | grep ^$ | grep ^#
# * 匹配前一個字元>=0次
# + 匹配前一個字元>=1次
```
* [Linux中搜尋檔案 whereis/locate/find](https://hackercat.org/linux/linux-whereis-locate-find)
## 應用
* line_notify.sh
```bash=
#!/bin/bash
# Line Notify Token
TOKEN="token內容"
# Alert Message
message="$1"
# 系統ip
sysip="系統ip"
# Send Message by notify-api
curl -X POST -H "Authorization: Bearer ${TOKEN}" -F "message=${sysip}-${message}" https://notify-api.line.me/api/notify
```
* [Linux 執行多個指令](https://www.ltsplus.com/linux/linux-run-multiple-commands)
* [Linux 執行目錄下所有 Script](https://www.ltsplus.com/linux/linux-run-all-scripts-in-directory)
* [Linux 列出檢視所有系統帳號](https://www.ltsplus.com/linux/linux-list-all-system-users)
* [Shell Script 讀取 Load Average](https://www.ltsplus.com/linux/shell-script-read-load-average)
* [藉由命令列模式使用LINE Notify發送訊息到LINE](https://engineering.linecorp.com/zh-hant/blog/using-line-notify-to-send-messages-to-line-from-the-command-line/)
* [ShellScript監控遠端主機,並透過Line告警](http://chienleebug.blogspot.com/2018/09/ubuntushellscriptline.html)
* [Linux 檢視 TLS/SSL 憑證到期日](https://www.ltsplus.com/linux/linux-check-tsl-ssl-cert-expiration-date)
* [Linux 使用 OpenSSL 指令檢查 TLS/SSL 憑證到期日教學與範例](https://officeguide.cc/linux-openssl-find-check-tls-ssl-certificate-expiry-date-tutorial-examples/)
* [How to check TLS/SSL certificate expiration date from command-line](https://www.cyberciti.biz/faq/find-check-tls-ssl-certificate-expiry-date-from-linux-unix/)
* [Bash script to calculate remaining days to expire SSL Certs for Website](https://krishnawattamwar.medium.com/bash-script-to-calculate-remaining-days-to-expire-ssl-certs-for-website-87df6ed2ef41)
* [How to check an SSL certificate expiration date from the linux command line](https://thejoyofstick.com/blog/2022/04/13/how-to-check-an-ssl-certificate-expiration-date-from-the-command-line/)
* [Matty9191/ssl-cert-check](https://github.com/Matty9191/ssl-cert-check)
## 其他補充資料
* [登入提示介面自定義](https://serversforhackers.com/c/customize-your-login-screen-via-linuxs-message-of-the-day-ubuntucentos)
* [邁向 RHCE 之路 系列](https://ithelp.ithome.com.tw/users/20035947/ironman/261)
* [How to Use Brace Expansion in Linux’s Bash Shell](https://www.howtogeek.com/725657/how-to-use-brace-expansion-in-linuxs-bash-shell/)
* [Bash Brace Expansion](https://www.linuxjournal.com/content/bash-brace-expansion)
## Windows的Powershell
* [Learning PowerShell](https://github.com/PowerShell/PowerShell/tree/master/docs/learning-powershell)
* [程式設計教學:使用 PowerShell](https://opensourcedoc.com/windows-programming/powershell/)
* [PowerShell 初學者快速入門教學](https://officeguide.cc/powershell-beginner-introduction/)
* [Powershell 學習筆記【黑暗執行緒】](https://blog.darkthread.net/blog/powershell-learning-notes/)
* [半自動稽核驗證 Powershell](https://hackmd.io/@Not/autoaudit)