--- 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` 為例 ![](https://hackmd.io/_uploads/rySwccAEs.png) * 外部指令,以 `mv` 為例 ![](https://hackmd.io/_uploads/ryGN9q0Vi.png) ### 內部指令 * 指內建在 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 ![](https://hackmd.io/_uploads/ByX_CeZUj.png) ::: ## 執行狀態回覆 (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代表指令執行有狀況。 ![](https://hackmd.io/_uploads/rJjgmjREo.png) :::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. ![](https://hackmd.io/_uploads/HkykaM1Hj.png) * 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 '$? - '$? ``` ![](https://hackmd.io/_uploads/HkcO2Sbrs.png) ## 各種指令介紹 ### test指令 * 檔案目錄存在與屬性檢查 > [Shell Script 測試檔案是否可讀或可寫入](https://www.ltsplus.com/linux/shell-script-check-file-readable-writeable) * `-e`:檢查檔案是否在在 * `-f`:檢查檔案是否存在 (且為正規檔案 regular file) ![](https://hackmd.io/_uploads/HkYYLZiHj.png) * `-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)