Bash腳本編程學習筆記04:測試命令test、狀態返回值、位置參數和特殊變數

来源:https://www.cnblogs.com/alongdidi/archive/2020/01/08/test_exitStatus_positionalAndSpecialParameter.html
-Advertisement-
Play Games

test簡介 測試命令test用於形成一個表達式,結合條件判斷語句if-else來判斷。 例如可以判斷某個文件是否存在,是否具備什麼樣的特性(可讀嗎?可寫嗎?可執行嗎?塊文件嗎?)等等。 測試命令test有三種語法格式: test EXPRESSION [ EXPRESSION ] [[ EXPRE ...


test簡介

測試命令test用於形成一個表達式,結合條件判斷語句if-else來判斷。

例如可以判斷某個文件是否存在,是否具備什麼樣的特性(可讀嗎?可寫嗎?可執行嗎?塊文件嗎?)等等。

測試命令test有三種語法格式:

test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]

前兩種是等價的,應該是沒有區別的。註意EXPRESSION兩邊與中括弧之間需要有空格。

雙中括弧與前兩者的區別,主要在於表達式中的操作符如果是這種情況的時候。

[[ string1 > string2 ]]
[[ string1 < string2 ]]

字元串之間比較比較大小,其實比的是在詞典中字元的先後順序。[]應該是基於ASCII來比較,而[[]]應該是基於shell當前的地理位置設置來比較(應該與locale相關的環境變數有關吧)。

更多的信息,可能需要大家去直接看bash的手冊吧,我稍微看了下,是真的很難以理解,所以暫時放棄了,只找到了這些簡單的區別,按照駿馬兄的話,咱只能先學習bash的形了,直接跳著看bash手冊會有一種被勸退的感覺。

測試表達式的結果為真或者假,真返回0,假返回1。這個返回值不會顯示在終端上,而是保存在shell的特殊變數“$?”中(bash原文中提到的是特殊參數(parameter),變數是一個通過名稱表示的參數)。

因此我們只需要在每次測試後echo這個特殊變數的值就可以驗證了。

# [ EXPRESSION ]
# echo $?

 

test實戰

數值比較

-eq:equal,是否相等。

[root@c7-server ~]# age=28
[root@c7-server ~]# [ $age -eq 28 ]
[root@c7-server ~]# echo $?
0

-ne:not equal,是否不相等。

[root@c7-server ~]# [ $age -ne 28 ]
[root@c7-server ~]# echo $?
1

-gt:greater than,是否大於。

[root@c7-server ~]# [ $age -gt 28 ]
[root@c7-server ~]# echo $?
1

-ge:greater equal,是否大於等於。

[root@c7-server ~]# [ $age -ge 28 ]
[root@c7-server ~]# echo $?
0

-lt:less than,是否小於。

[root@c7-server ~]# [ $age -lt 28 ]
[root@c7-server ~]# echo $?
1

-le:less equal,是否小於等於。

[root@c7-server ~]# [ $age -le 28 ]
[root@c7-server ~]# echo $?
0

字元串比較

==:是否等於。

[root@c7-server ~]# name=alongdidi
[root@c7-server ~]# [ $name == alongdidi ] [root@c7-server ~]# echo $? 0

!=:是否不等於。

[root@c7-server ~]# [ $name != alongdidi ]
[root@c7-server ~]# echo $?
1

=~:基於regex3的擴展正則匹配,並且使用這個操作符的時候必須使用[[]]。

[root@c7-server ~]# [[ $name =~ along(di){1} ]]
[root@c7-server ~]# echo $?
0
[root@c7-server ~]# [ $name =~ along(di){1} ]
-bash: syntax error near unexpected token `('
[root@c7-server ~]# [ $name =~ along\(di\){1} ]
-bash: [: =~: binary operator expected

-z:判斷字元串是否為空。

[root@c7-server ~]# [ -z $name ]
[root@c7-server ~]# echo $?
1

-n:判斷字元串是否不空。

[root@c7-server ~]# [ -n $name ]
[root@c7-server ~]# echo $?
0

在做字元串的等值比較時,無論是==、!=或者=~,它們都是二元(binary)的運算符,也就是在這個符號的左右兩邊都必須存在字元串。

如果其中一邊為空的話,就會報錯。

[root@c7-server ~]# unset name
[root@c7-server ~]# [ $name == tom ]
-bash: [: ==: unary operator expected

因為$name為空,因此整個表達式就變成了。

[ == tom ]

所以bash會報錯並告訴你我們期待的是一元(unary)運算符(因為只有tom這個字元串)。一元也可以叫單目,是一樣的意思。

解決的辦法有三個,對$name使用雙引號或者單引號包裹或者將[]換成[[]]。

[root@c7-server ~]# [ "$name" == tom ]
[root@c7-server ~]# echo $?
1
[root@c7-server ~]# [ '$name' == tom ]
[root@c7-server ~]# echo $?
1
[root@c7-server ~]# [[ $name == tom ]]
[root@c7-server ~]# echo $?
1

文件測試

存在性

-a FILE或者-e FILE:判斷文件是否存在。

[root@c7-server ~]# [ -a /etc/passwd ]
[root@c7-server ~]# echo $?
0
[root@c7-server ~]# [ -e /etc/passwd ]
[root@c7-server ~]# echo $?
0

文件類型

-b FILE:文件是否存在且為塊設備文件。

[root@c7-server ~]# ls -l /dev/sda1
brw-rw---- 1 root disk 8, 1 Jan  2 13:51 /dev/sda1
[root@c7-server ~]# [ -b /dev/sda1 ]
[root@c7-server ~]# echo $?
0

-c FILE:文件是否存在且為字元設備文件。

[root@c7-server ~]# ls -l /dev/autofs 
crw------- 1 root root 10, 235 Jan  2 13:51 /dev/autofs
[root@c7-server ~]# [ -c /dev/autofs ]
[root@c7-server ~]# echo $?
0

-d FILE:文件是否存在且為目錄文件。

[root@c7-server ~]# ls -ld /root
dr-xr-x---. 16 root root 4096 Jan  7 09:54 /root
[root@c7-server ~]# [ -d /root ]
[root@c7-server ~]# echo $?
0

-f FILE:文件是否存在且為普通文件(即文本文件)。

[root@c7-server ~]# ls -l /etc/passwd
-rw-r--r-- 1 root root 2296 Nov 11 14:28 /etc/passwd
[root@c7-server ~]# file /etc/passwd
/etc/passwd: ASCII text
[root@c7-server ~]# [ -f /etc/passwd ]
[root@c7-server ~]# echo $?
0

-h FILE:文件是否存在且為字元鏈接文件,即軟連接、符號鏈接。

-L FILE:同上。

[root@c7-server ~]# ls -l /etc/rc.local 
lrwxrwxrwx. 1 root root 13 Oct 17 14:59 /etc/rc.local -> rc.d/rc.local
[root@c7-server ~]# [ -h /etc/rc.local ]
[root@c7-server ~]# echo $?
0
[root@c7-server ~]# [ -L /etc/rc.local ]
[root@c7-server ~]# echo $?
0

-p FILE:文件是否存在且為命名管道文件。

[root@c7-server ~]# ls -l /run/dmeventd-client 
prw------- 1 root root 0 Jan  7 09:54 /run/dmeventd-client
[root@c7-server ~]# [ -p /run/dmeventd-client ]
[root@c7-server ~]# echo $?
0

-S FILE:文件是否存在且為套接字文件。

[root@c7-server ~]# ls -l /run/systemd/shutdownd
srw------- 1 root root 0 Jan  7 09:54 /run/systemd/shutdownd
[root@c7-server ~]# file /run/systemd/shutdownd
/run/systemd/shutdownd: socket
[root@c7-server ~]# [ -S /run/systemd/shutdownd ]
[root@c7-server ~]# echo $?
0

文件許可權

-r FILE:文件是否存在且對當前用戶可讀。

-w FILE:文件是否存在且對當前用戶可寫。

-x FILE:文件是否存在且對當前用戶可執行。

[root@c7-server ~]# ls -l /etc/passwd
-rw-r--r-- 1 root root 2296 Nov 11 14:28 /etc/passwd
[root@c7-server ~]# [ -r /etc/passwd ]
[root@c7-server ~]# echo $?
0
[root@c7-server ~]# [ -w /etc/passwd ]
[root@c7-server ~]# echo $?
0
[root@c7-server ~]# [ -x /etc/passwd ]
[root@c7-server ~]# echo $?
1

特殊文件許可權

-u FILE:文件是否存在且具有SUID許可權。

[root@c7-server ~]# ls -l /usr/bin/passwd
-rwsr-xr-x. 1 root root 27832 Jun 10  2014 /usr/bin/passwd
[root@c7-server ~]# [ -u /usr/bin/passwd ]
[root@c7-server ~]# echo $?
0

-g FILE:文件是否存在且具有SGID許可權。

[root@c7-server ~]# ls -l /usr/bin/wall
-r-xr-sr-x. 1 root tty 15344 Jun 10  2014 /usr/bin/wall
[root@c7-server ~]# [ -g /usr/bin/wall ]
[root@c7-server ~]# echo $?
0

-k FILE:文件是否存在且具有STICKY許可權。

[root@c7-server ~]# ls -ld /tmp/
drwxrwxrwt. 11 root root 4096 Jan  7 14:21 /tmp/
[root@c7-server ~]# [ -k /tmp/ ]
[root@c7-server ~]# echo $?
0

文件是否有內容測試

-s FILE:文件是否存在且有內容。

[root@c7-server ~]# ls -l test.txt
ls: cannot access test.txt: No such file or directory
[root@c7-server ~]# touch test.txt
[root@c7-server ~]# [ -s test.txt ]
[root@c7-server ~]# echo $?
1
[root@c7-server ~]# [ -s /etc/passwd ]
[root@c7-server ~]# echo $?
0

時間戳測試

-N FILE:文件自身從上一次讀操作後是否被修改過。

[root@c7-server ~]# cat test.txt
[root@c7-server ~]# [ -N test.txt ]
[root@c7-server ~]# echo $?
1
[root@c7-server ~]# echo "alongdidi" > test.txt 
[root@c7-server ~]# [ -N test.txt ]
[root@c7-server ~]# echo $?
0

從屬關係測試

-O FILE:當前用戶是否為文件的屬主。

-G FILE:當前用戶是否屬於文件的屬組。

[root@c7-server ~]# ls -ld /home/zwl/
drwx------. 3 zwl zwl 78 Apr 11  2018 /home/zwl/
[root@c7-server ~]# [ -O /home/zwl/ ]
[root@c7-server ~]# echo $?
1
[root@c7-server ~]# [ -G /home/zwl/ ]
[root@c7-server ~]# echo $?
1

雙目測試

FILE1 -ef FILE2:FILE1和FILE2是否指向同一個文件系統的相同inode的硬鏈接。

[root@c7-server ~]# ln test.txt test.hard
[root@c7-server ~]# ls -li test.txt test.hard 
33731123 -rw-r--r-- 2 root root 10 Jan  7 14:31 test.hard
33731123 -rw-r--r-- 2 root root 10 Jan  7 14:31 test.txt
[root@c7-server ~]# [ test.txt -ef test.hard ]
[root@c7-server ~]# echo $?
0

FILE1 -nt FILE2:FILE1是否新於FILE2,根據文件的mtime。

FILE1 -ot FILE2:FILE1是否舊於FILE2,根據文件的mtime。

[root@c7-server ~]# [ test.txt -nt /etc/passwd ]
[root@c7-server ~]# echo $?
0
[root@c7-server ~]# [ test.txt -ot /etc/passwd ]
[root@c7-server ~]# echo $?
1

組合測試條件

即與或非。

[ EXP1 -a EXP2 ]:EXP1和EXP2必須都為true,結果才為true。

[ EXP1 -o EXP2 ]:只要EXP1和EXP2當中有一個為true,結果就為true。

[ ! EXP ]:當EXP為true的時候,結果為false;當EXP為false的時候,結果為true。

練習

如果主機名為空或者包含local字元串,則將主機名設置為www.alongdidi.com。

hostName=$(hostname)
[ -z "${hostName}" -o ${hostName}=~"local" ] && hostname www.magedu.com

註:實際我在CentOS 7,Bash 4.2.46場景下執行該命令,得到的結果不太對。主要問題出在“=~”的判斷上。暫時未知如何解決,這裡大概知道下思路即可。

 

命令/腳本狀態返回值

上文中我們介紹了特殊變數$?,它存儲了測試表達式的測試結果。true=0,false=1。

命令執行的結果也會有這麼一個返回值(也可以叫退出狀態碼),一般返回值0表示命令執行成功,返回值非0(多數情況下是1)則表示失敗。

PS:這裡也需要註意,大多數編程語言使用1來表示成功/true等。還有大家也要註意和命令執行後的標準輸出或者標準錯誤輸出區別開。一個表示命令執行成功與否的結果,另一個則是命令執行的輸出結果。

這個返回值在我們執行腳本的時候,也會返回。預設腳本執行的返回值使用的是腳本中最後一條命令的返回值。

如果腳本中前幾條命令的執行均成功了,但是最後一條執行失敗了,那麼整個腳本的$?也是非0的。

我們可以通過exit命令來手工配置退出狀態碼。bash遇到exit會立即退出當前的shell並將返回值存入父shell的$?變數中。因此可以用來立即退出bash腳本。

[root@c7-server ~]# bash
[root@c7-server ~]# exit 10
exit
[root@c7-server ~]# echo $?
10

PS:有的時候退出狀態碼會異常,可能和返回值的取值有關係。

[root@c7-server ~]# exit 1000
exit
[root@c7-server ~]# echo $?
232
[root@c7-server ~]# bash
[root@c7-server ~]# exit 1024
exit
[root@c7-server ~]# echo $?
0

自定義返回值一般用於bash腳本中的判斷。比如,當某個文件不存在的時候,立即執行exit 5。

腳本會立刻退出,5這個返回值會被返回。一般程式員會事先定義好不同的返回值表達的不同含義,並將其寫入文檔。

用戶根據返回值和該文檔來判斷腳本為什麼中斷執行了。

像rsync的man手冊中就有定義。

0      Success
1      Syntax or usage error
2      Protocol incompatibility
3      Errors selecting input/output files, dirs
...

 

腳本中的參數

在學習C語言的時候,我們可以向函數執行傳遞參數的操作。bash腳本編程也是可以的。

可以對腳本進行傳參,也可以對函數。

在引用參數的時候,具體是引用腳本的參數還是函數的參數則取決於引用的位置。

按照馬哥課程的進度,函數還未學習到,因此這裡就不說了。(雖然已經有bash編程的基礎)等到寫bash函數的博文時,會在提及。

向腳本傳參和腳本中引用參數十分簡單,示例如下。

[root@c7-server ~]# cat test.sh 
#!/bin/bash
echo $1
echo $2
[root@c7-server ~]# bash test.sh along didi
along
didi

在執行腳本時,腳本名稱後面的字元串就是參數,多個參數之間以空格分離,根據參數出現在腳本名稱後的位置,在腳本中使用$1、$2、$3...來引用,它們也被稱作位置參數。

shift not shit

如果我們想要改變位置參數的位置,就需要使用到shift內置命令。

shift [n]

shift的本意是移動,我們可以理解為拿掉位置參數最左邊的n個。預設是1個。

[root@c7-server ~]# cat test.sh 
#!/bin/bash
echo $*
shift 2
echo $*
[root@c7-server ~]# bash test.sh a long di di
a long di di
di di

其他特殊變數

$#:獲取腳本被傳遞的參數的個數。

[root@c7-server ~]# cat test.sh 
#!/bin/bash
echo $#
[root@c7-server ~]# bash test.sh a l on g did i
6

$0:獲取腳本的名稱,這個名稱是執行時的名稱。執行的方式不同,值也不同。

[root@c7-server ~]# cat test.sh 
#!/bin/bash
echo $0
[root@c7-server ~]# bash test.sh 
test.sh
[root@c7-server ~]# /root/test.sh 
/root/test.sh
[root@c7-server ~]# ./test.sh 
./test.sh

只想獲取腳本的名稱的話,可結合basename命令。

[root@c7-server ~]# basename $(bash test.sh)
test.sh
[root@c7-server ~]# basename $(/root/test.sh)
test.sh
[root@c7-server ~]# basename $(./test.sh)
test.sh

$*:引用所有的參數。在雙引號的情況下,所有參數整合作為一個整體。

$@:引用所有的參數。無論是否有雙引號,每個參數自身都作為一個整體。

[root@c7-server ~]# cat test.sh 
#!/bin/bash
echo $*
echo $@
[root@c7-server ~]# bash test.sh a long di di
a long di di
a long di di

區別的話,可以看這個例子。

[root@c7-server ~]# cat test.sh
#!/bin/bash
for i in $*; do echo $i; done
for i in $@; do echo $i; done
echo "I am cut-off line"
for i in "$*"; do echo $i; done
for i in "$@"; do echo $i; done
[root@c7-server ~]# bash test.sh a long di di
a
long
di
di
a
long
di
di
I am cut-off line
a long di di
a
long
di
di

更深度的解釋,查閱官方文檔的話,需要對bash的單詞分割(word splitting)和IFS變數有理解才行。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 參考穀粒學院的linux視頻教程:http://www.gulixueyuan.com/course/300/task/7091/show ...
  • windows2016_x64搭建ELK(datasource filebeat logstash elasticsearch kibana) 1. 本文示例日誌程式基於asp.net core 3.0+nlog 2. ELK相關程式如下圖: 3. 日誌組件推薦: 客戶端安裝和運行 1. 下載fil ...
  • STM32F103是一款高性價比、多功能的單片機,配備常用的32位單片機片外資源,基於ARM Cortex-M3的32位處理器晶元,片內具有256KB FLASH,48KB RAM ( 片上集成12Bit A/D、D/A、PWM、CAN、USB、SDIO、FSMC等資源)。是應用的較為廣泛的一款單片 ...
  • 1、man find 解釋: find - search for files in a directory hierarchy,即:在目錄下查找文件 2、按文件被修改的時間查詢文件 命令格式: find 目錄 -mtime +n -name 文件名 在指定目錄及其子目錄下查找(負無窮,nowTime ...
  • 最近windows上需要配置全局代理 走 socks5,發現同類型的有 cow pcap 等解決方案,通過嘗試發現還是proxifier 比較好用! 下載:https://www.proxifier.com/download/legacy/ProxifierSetup331.exe 註冊: 激活ke ...
  • 安裝IIS7 1、打開伺服器管理器(開始-電腦-右鍵-管理-也可以打開),添加角色 直接下一步 勾選Web伺服器(IIS),下一步,有個註意事項繼續下一步(這裡我就不截圖了) 勾選ASP.NET會彈出以下視窗添加所需的角色服務,勾選CGI(這裡根據個人情況勾選,CGI是必選的,否則PHP不生效的) ...
  • 一、原理 1、Hypervisor是一種運行在物理伺服器和操作系統之間的中間軟體層,可允許多個操作系統和應用共用一套基礎物理硬體,它能直接訪問物理設備,會給每一臺虛擬機分配記憶體、CPU、網路、磁碟等資源,也可以確保虛擬機對應的硬體資源不被其他虛擬機訪問,是所有虛擬化技術的核心。 2、虛擬機 指通過軟 ...
  • 關於日誌切割 日誌文件包含了關於系統中發生的事件的有用信息,在排障過程中或者系統性能分析時經常被用到。對於忙碌的伺服器,日誌文件大小會增長極快,伺服器會很快消耗磁碟空間,這成了個問題。除此之外,處理一個單個的龐大日誌文件也常常是件十分棘手的事。logrotate是個十分有用的工具,它可以自動對日誌進 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...