Linux腳本(shell)的使用方式
概述
腳本:本質是一個文件,文件里面存放的是特定格式的指令,系統(tǒng)可以使用腳本解析器翻譯或解析指令并執(zhí)行(它不需要編譯)
- shell 既是一個用 C 語言編寫的應用程序,又是一種腳本語言(應用程序 解析 腳本語言)
- Shell 提供了一個界面,用戶通過這個界面訪問操作系統(tǒng)內核的服務。
- Ken Thompson 的 sh 是第一種 Unix Shell,Windows Explorer 是一個典型的圖形界面 Shell。
- Shell 編程跟 JavaScript、php 編程一樣,只要有一個能編寫代碼的文本編輯器和一個能解釋執(zhí)行的腳本解釋器就可以了。
Linux 的 Shell 命令解析器種類眾多,常見的有:
- Bourne Shell(/usr/bin/sh或/bin/sh)
- Bourne Again Shell(/bin/bash)(大多數(shù)Linux 系統(tǒng)默認的 Shell)
- C Shell(/usr/bin/csh)
- K Shell(/usr/bin/ksh)
- Shell for Root(/sbin/sh)
本文檔關注的是 Bash,也就是 Bourne Again Shell,由于易用和免費,Bash 在日常工作中被廣泛使用。同時,Bash 也是大多數(shù)Linux 系統(tǒng)默認的 Shell。查看自己linux系統(tǒng)的默認解析器命令:echo $SHELL
在一般情況下,人們并不區(qū)分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同樣也可以改為 #!/bin/bash。
語法詳解
編寫 shell 腳本文件的時候,最前面要加上一行:#!/bin/bash
,因為linux里面不僅僅只有bash一個解析器,還有其它的,它們之間的語法會有一些不同,所以最好加上這一句話,告訴系統(tǒng)要用這個解析器。
#! 是一個約定的標記,告訴系統(tǒng)其后路徑所指定的程序即是解釋此腳本文件的 Shell 程序。
注意:
- shell腳本中將多條命令換行時,命令是從上向下執(zhí)行,寫在上面的命令即使執(zhí)行錯誤,下面的命令也會繼續(xù)執(zhí)行。
- shell腳本中將多條命令寫在同一行時,使用分號( ; )分隔,寫在前面的命令即使執(zhí)行失敗,寫在后面的命令也會繼續(xù)執(zhí)行。
數(shù)學運算表達式
(( ))
是 Shell 數(shù)學計算命令,和 C++、C#、Java 等編程語言不同,在 Shell 中進行數(shù)據(jù)計算不那么方便,必須使用專門的數(shù)學計算命令,(( ))就是其中之一。
+ # 加 - # 減 * # 乘 / # 除 % # 取余 ** # 冪
Shell變量
變量分類
根據(jù)用途可以分為四種變量(變量的劃分,每本書都不相同):
環(huán)境變量:一組為系統(tǒng)內核、系統(tǒng)命令和應用程序提供運行環(huán)境而設定的變量的統(tǒng)稱
內部變量:特定為shell設定的一組變量的統(tǒng)稱
參數(shù)變量:傳參的數(shù)據(jù)。位置參數(shù)是傳給函數(shù),語句塊等等的數(shù)據(jù),可以通過$1
$2
… $N
以及配合shell內部變量(如$?
$@
等)進行引用
用戶自定義變量:用戶自己設置的變量。又可分為:局部變量和全局變量
局部變量:
- 只在代碼塊或函數(shù)有效,出了代碼塊或函數(shù),就消失的變量;
- 在代碼塊或函數(shù)中聲明的局部變量,必須通過 local 聲明,否則它也是對當前shell進程都可見的。
全局變量:
- 在腳本中定義,僅在當前Shell腳本中有效,其他Shell腳本進程不能訪問,其作用域從定義的位置開始,到腳本結束或被顯示刪除的地方為止。
- 全局變量可以升級成為臨時環(huán)境變量;通過export進行聲明,使當前進程的子進程也能使用這一變量。
- 臨時環(huán)境變量只對該運行環(huán)境有效,如果執(zhí)行另外一個shell腳本,這個臨時環(huán)境變量無能為力
環(huán)境變量和內部變量的區(qū)別和使用:
相同:
- 均為shell一啟動就加載
- 都是配合 $ 引用,并且在腳本中都是一開始就有的,不需要用戶再設定
不同:
- 環(huán)境變量可以添加、修改,用戶可以重新定義(詳細:https://blog.csdn.net/LLZK_/article/details/53813266)
- shell內部變量是固定不變的。
環(huán)境變量
環(huán)境變量是在操作系統(tǒng)中一個具有特定名字的對象,它包含了一個或多個應用程序將使用到的信息。
Linux是一個多用戶的操作系統(tǒng),每個用戶登錄系統(tǒng)時都會有一個專用的運行環(huán)境,通常情況下每個用戶的默認的環(huán)境都是相同的。這個默認環(huán)境就是一組環(huán)境變量的定義。每個用戶都可以通過修改環(huán)境變量的方式對自己的運行環(huán)境進行配置。
環(huán)境變量是和shell緊密相關的,用戶登錄系統(tǒng)后就啟動了一個shell,對于Linux來說一般是bash(Bourne Again shell,Bourne shell(sh)的擴展),也可以切換到其他版本的shell。
bash有兩個基本的系統(tǒng)級配置文件:/etc/bashrc和/etc/profile。這些配置文件包含了兩組不同的變量:shell變量和環(huán)境變量。shell變量是局部的,而環(huán)境變量是全局的。環(huán)境變量是通過shell命令來設置。設置好的環(huán)境變量又可以被所以當前用戶的程序使用。
環(huán)境變量的分類
- 根據(jù)環(huán)境變量的生命周期可以將其分為 永久性環(huán)境變量 和 臨時性變量
- 根據(jù)用戶等級的不同又可以將其分為 系統(tǒng)級變量 和 用戶級變量
對所有用戶生效的永久性變量(系統(tǒng)級):
這類變量對系統(tǒng)內的所有用戶都生效,所有用戶都可以使用這類變量。作用范圍是整個系統(tǒng)。
# 設置方式: # 使用 vi 命令打開 /etc/profile 文件,用export指令添加環(huán)境變量 # 步驟示例: # 1.打開配置文件,并按 i ,進入編輯模式 vi /etc/profile # 2.在配置文件末尾添加環(huán)境變量 export 環(huán)境變量名(一般大寫)="值" # 3.使配置文件立即生效 source /etc/profile # 注意: # 1. /etc/profile 只有root(超級用戶)才能修改??梢栽趀tc目錄下使用 ls -l 查看這個文件的用戶及權限 # 2. 添加新的環(huán)境變量并退出配置文件后,需要執(zhí)行命令 source /etc/profile 后才會立即生效。否則在下次重進此用戶時才能生效
對單一用戶生效的永久性變量(用戶級)
該類環(huán)境變量只對當前的用戶永久生效。也就是說假如用戶A設置了此類環(huán)境變量,這個環(huán)境變量只有A可以使用。而對于其他的B,C,D,E….用戶等等,這個變量是不存在的。
# 設置方法:在用戶主目錄”~”下的隱藏文件 “.bashrc”中添加自己想要的環(huán)境變量 # 步驟示例: # 1.打開配置文件,并按 i ,進入編輯模式 vi ~/.bashrc # 2.在配置文件末尾添加環(huán)境變量 export 環(huán)境變量名(一般大寫)="值" # 3.使配置文件立即生效 source ~/.bashrc # 注意: # 系統(tǒng)中可能存在兩個文件,.bashrc和.bash_profile(有些系統(tǒng)中只有其中一個) # 原則上來說設置此類環(huán)境變量時在這兩個文件任意一個里面添加都是可以的。二者設置大致相同 # ~/.bash_profile 是交互式login方式進入bash shell運行。即 .bash_profile 只會在用戶登錄的時候讀取一次 # ~/.bashrc 是交互式non-login方式進入bash shell運行。即 .bashrc 在每次打開終端進行一次新的會話時都會讀取 # 查看隱藏文件(.XXX): # 方式1:命令 ls -al # 方式2:命令 echo .*
臨時有效的環(huán)境變量(只對當前shell有效)
臨時環(huán)境變量作用域是當前shell腳本以及當前進程的子進程shell。退出當前shell腳本就消失了
# 設置方法:直接使用export指令添加。
環(huán)境變量的常用指令
# 查看顯示環(huán)境變量:echo,變量使用時要加上符號“$” #例: echo $PATH # 設置新的臨時環(huán)境變量 export export 新臨時環(huán)境變量名=內容 # 例: export MYNAME=”LLZZ” # 修改環(huán)境變量沒有指令,可以直接使用環(huán)境變量名進行修改。 # 例: MYNAME=”ZZLL” # 查看所有環(huán)境變量 env # 查看本地定義的所有shell變量 set # 刪除一個環(huán)境變量 unset 變量名 # 例: unset MYNAME
常用的環(huán)境變量(都為大寫)
PATH:查看命令的搜索路徑。通過設置環(huán)境變量PATH可以讓我們運行程序或指令更加方便。
# 查看環(huán)境變量PATH echo $PATH # 說明: # 每一個冒號都是一個路徑,這些搜索路徑都是一些可以找到可執(zhí)行程序的目錄列表。 # 當輸入一個指令時,shell會先檢查命令是否是內部命令,不是的話會再檢查這個命令是否是一個應用程序。 # 然后shell會試著從搜索路徑,即PATH中尋找這些應用程序。 # 如果shell在這些路徑目錄里沒有找到可執(zhí)行文件。則會報錯。 # 若找到,shell內部命令或應用程序將被分解為系統(tǒng)調用并傳給Linux內核。 # 示例: # 現(xiàn)在有一個c程序test.c通過gcc編譯生成的可執(zhí)行文件a.out(功能:輸出helloworld)。平常執(zhí)行這個a.out的時候是使用 # 方式1:相對路徑調用:./a.out (”.”代表當前目錄,”/”分隔符) # 方式2:絕對路徑調用:/home/lzk/test/a.out # 方式3:通過設置PATH環(huán)境變量,直接用文件名調用: a.out (只要可以通過PATH中路徑找得到這個可執(zhí)行文件) # 使用export指令添加PATH中的路徑 # 示例:將a.out的路徑添加到搜索路徑當中 export PATH=$PATH:路徑 # PATH中路徑是通過冒號“:”進行分隔的,把新的路徑加在最后就OK
HOME:指定用戶的主工作目錄,即為用戶登錄到Linux系統(tǒng)中時的默認目錄,即“~”
HISTSIZE:保存歷史命令記錄的條數(shù)。
- 用戶輸入的指令都會被系統(tǒng)保存下來,這個環(huán)境變量記錄的就是保持指令的條數(shù)。一般為1000
- 這些歷史指令都被保存在用戶工作主目錄 “~” 下的隱藏文件 .bash_profile 中
- 可以通過指令 history 來查看
LOGNAME:指當前用戶的登錄名
HOSTNAME:指主機的名稱
SHELL:指當前用戶用的是哪種shell
LANG/LANGUGE:和語言相關的環(huán)境變量,使用多種語言的用戶可以修改此環(huán)境變量
MAIL:指當前用戶的郵件存放目錄
PS1:第一級Shell命令提示符,root用戶是#,普通用戶是$
PS2:第二級Shell命令提示符,默認是“>”
PS3:第三級Shell命令提示符。主要用于select循環(huán)控制結構的菜單選擇提示符
TMOUT:用戶和系統(tǒng)交互過程的超時值,系統(tǒng)與用戶進行交互時,系統(tǒng)提示讓用戶進行輸入,但用戶遲遲沒有輸入,時間超過TMOUT設定的值后,shell將會因超時而終止執(zhí)行。
Shell內部變量
位置變量(參數(shù)變量)
當執(zhí)行一個Shell腳本的時候,如果希望命令行的到傳遞的參數(shù)信息,就要使用位置變量進行如:./myshell.sh 100 200 可以理解為shell腳本的傳參方法
# 預定義變量 # 功能描述 $n # n為數(shù)字 # $0表示命令本身(執(zhí)行腳本的命令) # $1-9代表第一個參數(shù)到第九個參數(shù) # 10以上的參數(shù)需要使用大括號進行包裹如:${10} $* # 傳遞給函數(shù)或腳本的所有參數(shù) $@ # 傳遞給函數(shù)或腳本的所有參數(shù) $# # 代表參數(shù)的個數(shù) # 注: # $* 和 $@ 都表示傳遞給函數(shù)或腳本的所有參數(shù),不被雙引號(" ")包含時,都以"$1" "$2" … "$n" 的形式輸出所有參數(shù)。 # 但是當它們被雙引號(" ")包含時, # "$*" 會將所有的參數(shù)作為一個整體,以"$1 $2 … $n"的形式輸出所有參數(shù); # "$@" 會將各個參數(shù)分開,以"$1" "$2" … "$n" 的形式輸出所有參數(shù)
預定義變量
預定義變量:shell 設計者事先已經定義好的變量,可以直接在 shell 腳本中使用
# 預定義變量 # 功能描述 $? # 命令執(zhí)行后返回的狀態(tài) $$ # 當前進程的進程號(PID) $! # 最近運行的一個后臺進程的進程號(PID) $? # 最后一次執(zhí)行的命令的返回狀態(tài) # 若返回 0 :則上一個命令正確執(zhí)行;若返回 非0(具體數(shù)值由命令自己決定),則上一個命令未正常執(zhí)行 $LINENO # 調測用。用于顯示腳本中當前執(zhí)行的命令的行號 $OLDPWD # 配合cd命令改換到新目錄之前所在的工作目錄 # 用法:cd $OLDPWD (切換到之前的工作目錄,和cd - 功能一樣) $PPID # 當前進程的父進程的PID $PWD # 當前工作目錄。等同于命令pwd的輸出 $RANDOM # 隨機數(shù)變量。每次引用這個變量會得到一個0~32767的隨機數(shù) $REPLY # 通過read命令讀入的數(shù)據(jù),如果沒有被賦值指定變量,則默認賦值到 REPLY 變量中 $SECONDS # 腳本已經運行的時間(以秒為單位)
自定義變量:定義、賦值
變量是任何一種編程語言都必不可少的組成部分,變量用來存放各種數(shù)據(jù)。腳本語言在定義變量時通常不需要指明類型,直接賦值就可以,Shell 變量也遵循這個規(guī)則。
在 Bash shell 中,每一個變量的值都是字符串,無論變量賦值時有沒有使用引號,值都會以字符串的形式存儲。
shell變量和一些編程語言不同,一般shell的變量賦值的時候不用帶“$”
,而使用或者輸出的時候要帶“$”
。加減乘除的時候要加兩層小括號。括號外面要有一個“$”
,括號里面的變量可以不用“$”
。
定義變量
Shell 支持以下三種定義變量的方式:
variable=value variable='value' variable="value"
說明:
- variable 是變量名,value 是賦給變量的值
- 如果 value 不包含任何空白符(例如空格、Tab 縮進等),那么可以不使用引號
- 如果 value 包含了空白符,那么就必須使用單、雙引號包圍起來。
- 分析:讀完命令之后,會對字符串或關鍵字按照空格切割,切割之后,分為了兩個部分:
c=he
和llo
,c=he
被理解為一個變量賦值,而llo
卻找不到匹配的項,并且檢索不到相關的命令,所以就會輸出這個llo
的報錯。 - shell腳本中的變量類型只有整型和字符串
注意:
- 賦值號
=
的周圍不能有空格,否則會被解析成命令,報錯無此命令。這可能和常見的大部分編程語言都不一樣。
Shell 變量的命名規(guī)范:
- 變量名由數(shù)字、字母、下劃線組成;
- 必須以字母或者下劃線開頭;
- 不能使用 Shell 里的關鍵字(通過 help 命令可以查看保留關鍵字)。
單引號和雙引號賦值的區(qū)別:
定義變量時,變量的值時使用單引號' '
包圍和雙引號" "
包圍的區(qū)別:
- 以單引號
' '
包圍變量的值時,單引號里面是什么就輸出什么,即使內容中有變量和命令(命令需要反引起來)也會把它們原樣輸出。這種方式比較適合定義顯示純字符串的情況,即不希望解析變量、命令等的場景。 - 以雙引號
" "
包圍變量的值時,輸出時會先解析里面的變量和命令,而不是把雙引號中的變量名和命令原樣輸出。這種方式比較適合字符串中附帶有變量和命令并且想將其解析后再輸出的變量定義。
建議:
- 如果變量的內容是數(shù)字,可以不加引號
- 如果需要原樣輸出就加單引號
- 其他沒有特別要求的字符串等最好都加上雙引號。定義變量時加雙引號是最常見的使用場景
示例:
#!/bin/bash a=10 b=20 c="this is a test" d=$((a+b)) f=test # 變量賦值的時候如果只有一個單詞可以不用加引號 time=`date` # date 命令用來獲得當前的系統(tǒng)時間 date=`date +%s` # data 命令的 %s 格式控制符可以得到當前的 UNIX 時間戳,可以用于計算腳本的運行時間 # UNIX 時間戳是指從 1970 年 1 月 1 日 00:00:00 到目前為止的秒數(shù) echo $c echo "a = "$a # 輸出a的 echo "a+b = "$((a+b)) # 輸出a+b的值 echo $((a+b*a-b/a+a%b+a**2)) #表達式可以很長
變量的賦值
# 方式1:“=”并初始化 var=value # 注意:如果value是帶有空格的字符串,需要加單或雙引號 # 方式2:“=”不初始化 var= # 未賦值變量,值為null # 方式3:read命令 # read命令是讀取標準輸入的數(shù)據(jù),然后存儲到指定的變量中。注意:read接收的是標準輸入而不是參數(shù),read命令是不接收參數(shù)的。
自定義變量:引用、修改、刪除
變量的引用
# 方式1 $variable # 方式2(變量名外面的花括號 { }是可選的,加不加都行,加花括號是為了幫助解釋器識別變量的邊界)。推薦 ${variable} # 方式3 “$variable”或“${variable}” # 注:引用變量后,雙引號會對于引用結果中空格和一些特殊符號進行的解析或者不解析。而引用結果用的空格在命令中的分隔作用會完全改變一個命令的輸出,而特殊符號的是否解析也會影響最終的輸出結果。
skill="Java" echo "I am good at ${skill}Script" # 如果不給 skill 變量加花括號,寫成`echo "I am good at $skillScript"`,解釋器就會把 $skillScript 當成一個變量(其值為空),代碼執(zhí)行結果就不是我們期望的樣子了。
修改變量的值
已定義的變量,可以被重新賦值,如:
url="http://c.biancheng.net" echo ${url} # 第二次對變量賦值時不能在變量名前加 $,只有在使用變量時才能加 $ url="http://c.biancheng.net/shell/" echo ${url}
刪除變量
使用 unset 命令可以刪除變量。
語法:
unset variable_name
注意:
- 變量被刪除后不能再次使用
- unset 命令不能刪除只讀變量
示例:
#!/bin/sh myUrl="http://c.biancheng.net/shell/" unset myUrl echo $myUrl # 會沒有任何輸出
自定義變量:設置只讀
使用 readonly 命令可以將變量定義為只讀變量,只讀變量的值不能被改變。
下面的例子嘗試更改只讀變量,結果報錯:
#!/bin/bash myUrl="http://c.biancheng.net/shell/" readonly myUrl myUrl="http://c.biancheng.net/" # 會報錯
Shell命令替換:將命令的結果賦值變量
Shell 也支持將命令的執(zhí)行結果賦值給變量,常見的有以下兩種方式:
# 兩種方式可以完成命令替換,一種是$(),一種是反引號` ` variable=$(commands) variable=`commands` # 說明: # 1.variable 是變量名,commands 是要執(zhí)行的命令 # 2.commands 可以只有一個命令,也可以有多個命令,多個命令之間以分號;分隔
注意:
如果被替換的命令的輸出內容包括多行(也即有換行符),或者含有多個連續(xù)的空白符,那么在輸出變量時應該將變量用雙引號包圍,否則系統(tǒng)會使用默認的空白符來填充,這會導致?lián)Q行無效,以及連續(xù)的空白符被壓縮成一個,出現(xiàn)格式混亂的情況。
兩種變量替換的形式是等價的,可以隨意使用
反引號和單引號非常相似,容易產生混淆,所以不推薦使用這種方式;
使用 $()
相對清晰,有些情況也必須使用 $()
,比如$()
支持嵌套,反引號不行
$()
僅在 Bash Shell 中有效,而反引號可在多種 Shell 中使用。
Shell變量表達式
# 表達式 # 說明 ${#string} # 計算$string的長度 ${string:position} # 從pos位置開始提取字符串 ${string:position:len} # 從pos位置開始提取長度為len的字符串 ${string#substr} # 從開頭刪除最短匹配子串 ${string##substr} # 從開頭刪除最長匹配子串 ${string%substr} # 從結尾刪除最短匹配子串 ${string%%substr} # 從結尾刪除最長匹配子串 # 注意:字符串的長度包括空格,但是沒有像C語言中那種'\0'字符
示例
#!/bin/bash str="a b c d e f g h i j" echo "the source string is "${str} #源字符串 echo "the string length is "${#str} #字符串長度 echo "the 6th to last string is "${str:5} #截取從第五個后面開始到最后的字符 echo "the 6th to 8th string is "${str:5:2} #截取從第五個后面開始的2個字符 echo "after delete shortest string of start is "${str#a*f} #從開頭刪除a到f的字符 echo "after delete widest string of start is "${str##a*} #從開頭刪除a以后的字符 echo "after delete shortest string of end is "${str%f*j} #從結尾刪除f到j的字符 echo "after delete widest string of end is "${str%%*j} #從結尾刪除j前面的所有字符包括j
shell測試判斷:test 、 [ ] 、[[ ]]
Shell中的 test 命令和 [ ] 用于檢查某個條件是否成立,它可以進行數(shù)值、字符和文件三個方面的測試。
[[ ]] 是 bash 程序語言的關鍵字。并不是一個命令,[[ ]] 結構比[ ]結構更加通用。在 [[ 和 ]] 之間所有的字符都不會發(fā)生文件名擴展或者單詞分割,但是會發(fā)生參數(shù)擴展和命令替換。支持字符串的模式匹配,使用 =~ 操作符時甚至支持shell的正則表達式。字符串比較時可以把右邊的作為一個模式,而不僅僅是一個字符串,比如 [[ hello == hell? ]] ,結果為真。[[ ]] 中匹配字符串或通配符,不需要引號。
使用 [[ ]] 條件判斷結構,而不是 [ ],能夠防止腳本中的許多邏輯錯誤。比如:&&、||、<、> 操作符能夠正常存在于 [[ ]] 條件判斷結構中,但是如果出現(xiàn)在 [ ] 結構中的話,會報錯。比如可以直接使用 if [[ $a !=1 && $a != 2 ]]
,如果不使用雙括號, 則為 if [ $a -ne 1] && [ $a != 2 ]
或者 if [ $a -ne 1 -a $a != 2 ]
。
bash把雙中括號中的表達式看作一個單獨的元素,并返回一個退出狀態(tài)碼。
注意:使用 [ ] 的時候必須要每個變量之間都要有空格,和左右中括號也要有空格,否則報錯。
數(shù)值測試
參數(shù) | 說明 |
---|---|
-eq | 等于則為真 |
-ne | 不等于則為真 |
-gt | 大于則為真 |
-ge | 大于等于則為真 |
-lt | 小于則為真 |
-le | 小于等于則為真 |
字符串測試
參數(shù) | 說明 |
---|---|
= | 等于則為真 |
!= | 不相等則為真 |
-z 字符串 | 字符串的長度為零則為真 |
-n 字符串 | 字符串的長度不為零則為真 |
文件測試
參數(shù) | 說明 |
---|---|
-e 文件名 | 如果文件存在則為真 |
-f 文件名 | 如果文件存在且為普通文件則為真 |
-d 文件名 | 如果文件存在且為目錄則為真 |
-r 文件名 | 如果文件存在且可讀則為真 |
-w 文件名 | 如果文件存在且可寫則為真 |
-x 文件名 | 如果文件存在且可執(zhí)行則為真 |
-s 文件名 | 如果文件存在且至少有一個字符則為真 |
-c 文件名 | 如果文件存在且為字符型特殊文件則為真 |
-b 文件名 | 如果文件存在且為塊特殊文件則為真 |
示例
#!/bin/bash # 文件測試 echo "Please input two numbers:" read num1 read num2 echo "num1 = "${num1} echo "num2 = "${num2} echo -e "by test\n" test $num1 -eq $num2 && echo "num1 == num2" || echo "num1 != num2" echo -e "by []\n" [ $num1 -eq $num2 ] && echo "num1 == num2" || echo "num1 != num2" # 數(shù)值測試 echo "Please input a filename: " # 從標準輸入獲取一個文件名,并存入filename變量中 read filename echo -e "by test\n" test -f $filename && echo "這是一個普通文件" || echo "這不是一個普通文件" echo -e "by []\n" [ -f $filename ] && echo "這是一個普通文件" || echo "這不是一個普通文件"
邏輯操作符:與、或、非
Shell 還提供了 與( -a )、或( -o )、非( ! ) 三個邏輯操作符用于將測試條件連接起來,其優(yōu)先級為: ! 最高, -a 次之, -o 最低。
示例:
#!/bin/bash # 邏輯操作符和字符串測試 echo "Please input a city name: " # 從標準輸入獲取一個值,并存入city變量中 read city if "成都" = $city -o "南京" = $city then echo '城市是成都或南京' else echo '城市既不是成都也不是南京' fi
shell條件分支結構語句
單分支判斷語句
格式:
if 條件 ; then 結果; fi # 最后面一定要有fi,在shell腳本里面,控制分支結構結束都要和開頭的單詞相反,例如,if <–> fi,case <–> esac
示例
#!/bin/bash echo "Please input a filename" # read filename:表示從標準輸入獲取一個文件名,并存入felename變量中 read filename if [ -f $filename ] then echo "this file is a ordinary file." fi
雙分支判斷語句
格式:
if 條件 ; then 結果; else 結果; fi
示例
#!/bin/bash echo "Please input a filename" read filename if [ -f $filename ] then echo "this file is a ordinary file." else echo "this file is not a ordinary file." fi
多分支判斷語句
多分支判斷有兩種(和C語言的一樣 ):if-else if 和 case
語法:
if 條件 ; then 結果; elif 條件; then 結果; else 結果; fi
if-else if 示例
#!/bin/bash echo "Please input your math grades" read grades if [ $grades -gt 100 ] || [ $grades -lt 0 ];then echo "Please input the number range in 0 - 100" fi if [ $grades -ge 90 ] && [ $grades -le 100 ] then echo "Your grade is excellent." elif [ $grades -ge 80 ] && [ $grades -le 89 ];then echo "Your grade is good." elif [ $grades -ge 70 ] && [ $grades -le 79 ];then echo "Your grade is middle." elif [ $grades -ge 60 ] && [ $grades -le 69 ];then echo "Your grade is passing." else echo "Your grade is badly." fi
case 示例
#!/bin/bash echo "Please input a command" read cmd case $cmd in cpu) echo "The cpu information is" cat /proc/cpuinfo;; mem) echo "The mem information is" cat /proc/meminfo;; device) echo "The device information is" cat /proc/scsi/device_info;; CD-ROM) echo "The CD-ROM information is" cat /proc/sys/dev/cdrom/info;; *) echo "Your input command is invalid" esac
shell循環(huán)語句
for語句
格式:
for 變量 in 列表 do 語句 done
示例
#!/bin/bash arr=("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c" "e" "e" "f") # 遍歷(不帶數(shù)組下標。* 和 @ 均可) for value in ${arr[*]} do echo $value done # 遍歷(帶數(shù)組下標) for (( i = 0 ; i < ${#arr[@]} ; i++ )) do echo ${arr[$i]} done
while語句
while語句是只要條件為真就執(zhí)行下面語句。
格式:
while 條件 do 語句 done
需要注意的是,這里的條件除了 while true 可以這樣寫,其它的條件都要用 test或者 []來判斷
示例
# -----------文件名為test.sh----------- #!/bin/bash # $1 為調用腳本時的第1個傳參 i=$1 while [ $i -gt 0 ] do echo $i ((i--)) done # -----------調用----------- sh test.sh 10 # 文件名test.sh 后面跟的是參數(shù),$0代表文件名,$1代表第一個參數(shù)...
until語句
until語句是只要條件為假就執(zhí)行下列語句
格式:
until 條件 do 語句 done
示例
#!/bin/bash i=$1 until [ $i -le 0 ] do echo $i ((i--)) done
Shell函數(shù)
格式:
[function] funcName() {undefined 語句 [return 返回值] }
說明:
- Shell 函數(shù) return的返回值只能是整數(shù),一般用來表示函數(shù)執(zhí)行成功與否,0表示成功,其他值表示失敗。如果 return 其他數(shù)據(jù),比如一個字符串,報錯提示:“numeric argument required”。
return 返回值
是可選的;如果沒有return,則默認返回最后一條語句執(zhí)行成功與否的狀態(tài)值- 傳參到函數(shù)中使用位置參數(shù);位置參數(shù)在函數(shù)中的使用:從
$1
到$n
,$0
是文件名
函數(shù)調用方式:
# 方式1:調用函數(shù),然后再使用預定義變量 $? 獲取函數(shù)返回值 function_name [arg1 arg2 ......] $? # 方式2:調用函數(shù)并將函數(shù)的標準輸出賦值到一個變量中。注意:不是返回值 value=`function_name [arg1 arg2 ......]` value=$(function_name [arg1 arg2 ......])
示例:
#!/bin/bash #打印數(shù)字 printNum() { echo $1 # 位置參數(shù)的使用 } for i in `seq 2 8` # seq是一個命令,順序生成一串數(shù)字或者字符 do printNum $i # 位置參數(shù)的傳參 done
函數(shù)輸出字符串的方式:
使用反引號``
調用的方式將函數(shù)的標準輸出賦值到一個變量中,腳本在需要的時候訪問這個變量來獲得函數(shù)的輸出值。
注意:若函數(shù)內進行了多次標準輸出,則會將所有的輸出值一起賦值到變量中。推薦函數(shù)內只進行一次標準輸出。
#!/bin/bash # 函數(shù)輸出字符串 getString() { echo "abc" return 0 } # 函數(shù)輸出賦值 value=`getString` # 使用 echo ${value}-xxxx
Shell腳本的執(zhí)行方式
(以下方式,指定腳本可以使用絕對路徑,也可以使用相對路徑)
路徑/xxx.sh:先按照文件中 #! 指定的解析器解析,如果 #! 指定指定的解析器不存在 才會使用系統(tǒng)默認的解析器。
注意:
- 這種方式需要被執(zhí)行文件有可執(zhí)行權限(x)(
chmod +x 文件名
:給文件的所有身份都加上執(zhí)行權限),否則報錯 - 該方式,即使腳本就在當前路徑,也必須指定:./xxx.sh
bash xxx.sh:指明先用bash解析器解析,如果bash不存在才會使用默認解析器
sh xxx.sh:直接使用默認解析器解析??梢允褂孟鄬β窂剑部梢允褂媒^對路徑
. xxx.sh:直接使用默認解析器解析注意:.(點)和文件名直接有一個空格
拓展
獲取本機ip地址腳本命令
方法一:ip addr
多網卡情況也是返回第一個IP
ip addr | awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}' | sed -n '1p'
shell方法二:ifconfig -a
ifconfig -a | grep inet | grep -v 127.0.0.1 | grep -v inet6 | awk '{print $2}' | tr -d "addr:" # 命令解析 - ifconfig -a 和window下執(zhí)行此命令一樣道理,返回本機所有ip信息 - grep inet 截取包含ip的行 - grep -v 127.0.0.1 去掉本地指向的那行 - grep -v inet6 去掉包含inet6的行 - awk { print $2} $2 表示默認以空格分割的第二組 同理 $1表示第一組 - tr -d "addr: 刪除"addr:"這個字符串
多網卡情況
倘若有多個網卡,可能會出現(xiàn)多個不同網段的IP,這個時候如果還是執(zhí)行上述命令就會返回多個IP,如下:
假設某個機器有192...8和10...*網段的IP,現(xiàn)在要實現(xiàn)不同網段的IP地址打印不同的輸出,shell腳本如下
#!/bin/sh ip=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"?` echo $ip if[[ $ip =="10."*]] then echo "該網段是10.*.*.*網段" else echo "該網段是192.*.*.*網段" fi
jq命令:操作 JSON
jq命令允許直接在命令行下對JSON進行操作,包括分片、過濾、轉換等
jq是用C編寫。
jq的預編譯的二進制文件可以直接在Linux、OS X和windows系統(tǒng)上運行,在linux系統(tǒng)中可以直接用yum安裝。
準備json串:kumufengchun.json
{ "name":"kumufengchun", "age":"18", "city":"beijing", "email":"kumufengchun@gmail.com", "date":"Thursday", "country":"China", "company":["baidu","google","alibaba"] }
準備json串:
[ { "name":"JSON", "good":true }, { "name":"XML", "good":false } ]
jq基本使用
# 用jq .直接查看json文件內容 # 方式1 jq . kumufengchun.json # 方式2 cat kumufengchun.json | jq . # 輸出某個字段或者某個索引的值 # 語法: jq '.<key>' # 這里key是字段名稱 # 實例 jq .name kumufengchun.json # 輸出:"kumufengchun" # 使用 -r 參數(shù),不含雙引號輸出結果 jq -r .name kumufengchun.json # 輸出: kumufengchun # 輸出數(shù)組的值 # 語法: jq '.<key>[<value>]' # 這里value是數(shù)組的索引整數(shù)值 # 實例 jq '.company[0]' kumufengchun.json # 輸出列表、數(shù)組的一部分,對其進行切片 # 語法: jq '.<list-key>[s:e]' # 返回的是數(shù)組或者列表的index從s開始(包括s)到e結束(不包括e) # 實例 jq '.company[0:2]' kumufengchun.json # 也可以省略開始的index,只有結束的index,如下,仍然是不包括結束index的值 jq '.company[:3]' kumufengchun.json # 也可以省略結束的index,只有開始的index,如下,輸出到最后 jq '.company[1:]' kumufengchun.json # 開始的索引也可以是負數(shù),表示從后邊倒著數(shù),從-1開始數(shù) jq '.company[-2:]' kumufengchun.json # 循環(huán)輸出所有的值,如數(shù)組嵌套 # 語法: jq '.[]' # 實例 jq '.[]' test.json # 輸出多個索引的值,可以用逗號分割 # 語法: jq '.key1,.key2' # 實例 jq '.name,.age' kumufengchun.json # 如果是數(shù)組,用中括號括起來要輸出的鍵值,鍵值先寫誰,先輸出誰 jq '.company[2,0]' kumufengchun.json # 用管道符號|可以對其進行再次處理 # 語法: jq .[] | .<key1> # 實例 jq '.[]|.name' test.json # 括號的作用 echo 1 | jq '(.+2)*5' # 輸出: 15 echo {1,2,3} | jq '(.+2)*5' # 輸出: 15 20 25 # length求長度,如果是字符串是求的字符串的長度,如果是數(shù)組則求得是數(shù)組的長度 cat kumufengchun.json | jq '.[] | length' # 輸出所有的keys # 語法: jq keys # 實例 cat kumufengchun.json | jq 'keys' # 輸出數(shù)組的keys(索引) cat kumufengchun.json | jq '.company | keys' # 判斷存不存在某個鍵 cat kumufengchun.json | jq 'has("email")' # 輸出: true
獲取 tee 前一個命令返回值
管道中的命令,使用 $?
只能獲取管道中最后一條命令的返回值
PIPESTATUS[n],獲取管道中第n個命令的返回值
示例:
cp abc def 2>&1 | tee a.log # ${PIPESTATUS[0]} 獲取的是 cp 命令的返回值 test ${PIPESTATUS[0]} -ne 0 && exit
eval:執(zhí)行字符串命令
#!/bin/bash cmd="mkidr aaa" eval $cmd
字符串截取
var=http://www.aaa.com/123.htm # 1. # 號截取,刪除左邊字符,保留右邊字符 echo ${var#*//} # 其中 var 是變量名,# 號是運算符,*// 表示從左邊開始刪除第一個 // 號及左邊的所有字符 # 即刪除 http:// # 結果是 :www.aaa.com/123.htm # 2. ## 號截取,刪除左邊字符,保留右邊字符。 echo ${var##*/} # ##*/ 表示從左邊開始刪除最后(最右邊)一個 / 號及左邊的所有字符 # 即刪除 http://www.aaa.com/ # 結果是 123.htm # 3. %號截取,刪除右邊字符,保留左邊字符 echo ${var%/*} # %/* 表示從右邊開始,刪除第一個 / 號及右邊的字符 # 結果是:http://www.aaa.com # 4. %% 號截取,刪除右邊字符,保留左邊字符 echo ${var%%/*} # %%/* 表示從右邊開始,刪除最后(最左邊)一個 / 號及右邊的字符 # 結果是:http: # 5. 從左邊第幾個字符開始,及字符的個數(shù) echo ${var:0:5} # 其中的 0 表示左邊第一個字符開始,5 表示字符的總個數(shù)。 # 結果是:http: # 6. 從左邊第幾個字符開始,一直到結束。 echo ${var:7} # 其中的 7 表示左邊第8個字符開始,一直到結束。 # 結果是 :www.aaa.com/123.htm # 7. 從右邊第幾個字符開始,及字符的個數(shù) echo ${var:0-7:3} # 其中的 0-7 表示右邊算起第七個字符開始,3 表示字符的個數(shù)。 # 結果是:123 # 8. 從右邊第幾個字符開始,一直到結束 echo ${var:0-7} # 表示從右邊第七個字符開始,一直到結束。 # 結果是:123.htm # 注:(左邊的第一個字符是用 0 表示,右邊的第一個字符用 0-1 表示)
父腳本捕捉子腳本(子進程)的中止
- exit 0:正常運行程序并退出程序;
- exit 1:非正常運行導致退出程序;exit 后面數(shù)值大于0,均為非正常退出
子腳本手動進行中止操作
#!/bin/bash # 子腳本 echo aaa && exit 1
父腳本通過 $?
(有 tee 命令時,使用${PIPESTATUS[0]}`)命令獲取子腳本的返回值
Shell腳本加載另一個腳本
#!/bin/bash # 方式1:source # 注意:被加載的腳本,不能缺省路徑 source ./first.sh # 方式2:點號(.) # 注意:1.被加載的腳本,不能缺省路徑。2.點號與腳本文件之間記得要有空格 . ./first.sh
- 使用source命令和點號(.)是等價的,類似于C/C++中的#include預處理指令,都是將指定的腳本內容加載至當前的腳本中,由一個Shell進程來執(zhí)行。
- 使用sh命令來調用另外的腳本,會開啟新的Shell進程來執(zhí)行指定的腳本,父進程中的變量在子進程中無法被訪問到。
啟用子shell線程
子shell在linux腳本中使用()實現(xiàn),即在()中的代碼會在子shell中執(zhí)行
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Centos7如何備份和還原Redis數(shù)據(jù)的方法
這篇文章主要介紹了Centos7如何備份和還原Redis數(shù)據(jù)的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06CentOS Linux 下配置Apache2+PHP5+MySQL5+GD庫的方法
先安裝MYSQL 服務器再安裝GD庫基本包2008-04-04詳解linux系統(tǒng)目錄sys,tmp,usr,var!
在本篇文章里小編給大家詳解了關于linux系統(tǒng)目錄,sys,tmp,usr,var!的相關知識點內容,有興趣的朋友們參考下。2019-06-06在Ubuntu/Linux環(huán)境下使用MySQL開放/修改3306端口和開放訪問權限
這篇文章主要介紹了在Ubuntu/Linux環(huán)境下使用MySQL開放/修改3306端口和開放訪問權限,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07Linux網絡啟動問題:Device does not seem to be present解決辦法
這篇文章主要介紹了Linux網絡啟動問題:Device does not seem to be present解決辦法的相關資料,希望通過本文能幫助到大家解決這樣的問題,需要的朋友可以參考下2017-10-10Linux環(huán)境下實現(xiàn)多進程Socket通信功能
在網絡編程中,服務器與多個客戶端之間的通信是一個常見的需求,本文將介紹如何在Linux環(huán)境下實現(xiàn)一個服務器程序,該程序能夠啟動多個客戶端進程,并通過Socket與這些客戶端進行通信,需要的朋友可以參考下2025-05-05