1. 程式人生 > >前端開發弄懂了這些shell命令就足夠了

前端開發弄懂了這些shell命令就足夠了

最新內容我會即時在github更新,建議在github閱讀

1.set命令

set通過選項來開關shell的不同特性,每個特性都對應一個選項。每個特性都有兩種配置方式:

(1)一種是通過 set -e 和 set +e 這種形式,即直接指定選項。

(2)另一種是通過 set -o errexit 和 set +o errexit 這種形式,即通過 o 這個選項來指定選項名。

我想你一定對選項是用 + 號還是 - 號十分好奇。在set命令中,選項前面跟著 - 號表示開啟這個選項, + 表示關閉這個選項。下面給出一個set命令的例子:

#!/bin/bash
set -o xtrace
# 設定了這個選項之後,對於每一條要執行的命令,shell在擴充套件了命令之後(引數擴充套件)、執行命令之前輸出trace到stderr。
set -o errexit # 可以把這樣註釋掉看下執行效果有什麼不一樣。 echo "Before" ls # ls也不存在的檔案 echo "After"

下面給出幾個例子:

例子1:noclobber的例子

set -o noclobber
#如果檔案不存在那麼我們會建立,如果已經存在那麼我們不會覆蓋而是報錯
declare path=$(cd `dirname $0`;pwd)
#獲取到bash檔案所在的目錄,並將該路徑賦值給path
echo "Bash file path:" $path
echo "kick" > $path/temp.txt
set +o noclobber
//關閉選項noclobber,此時如果檔案存在那麼內容會被覆蓋
echo
"kick noclobber close" > $path/temp.txt

noclobber特性可以防止重定向時不經意地重寫了已存在的檔案。通過設定變數noclobber可以將此特性開啟。開啟後若將輸出重定向到某個已存在檔案,則shell將報告錯誤訊息,並且不執行重定向命令。

例子2:ignoreeof例項

set –o ignoreeof
#用於互動式shell指令碼,等待深入研究,如電視開發互動營銷等

ignoreeof變數用來禁止使用ctrl d來退出shell(ctrl d不但用來退出shell,而且能夠終止使用者直接輸往標準輸出上的輸入。該操作經常在一些shell實用命令中使用,例如實用命令cat。在這些實用程式 操作中,很容易誤操作而意外地退出shell。ignoreeof特別變數正是用來防止這種意外的退出。設定該選項之後,使用者只能用logout

exit命令退出shell。

例子3:noglob例項

set -o noglob
#關閉萬用字元
declare path=$(cd `dirname $0`;pwd)
ls -ahl -n $path/noglo[bc]
echo "Next is noglob closed:"
set +o noglob
#開啟萬用字元
ls -ahl -n $path/noglo[bc]

配置noglob變數後,shell將不擴充套件文件名中一些特別的字元或字串。如字元*、?、[]等將不再作為萬用字元。假如使用者希望列出結尾為?的文件名 answer?,可通過如下步驟:首先,使用者使noglob變數為無效,然後再列出文件名。下面是windows下的輸出結果:

 ls: cannot access '/c/Users/Administrator/Desktop/shellGlobStar/src/set/noglo[bc]': No such file or directory
 <!-- 因為我們上面關閉了萬用字元,所以noglo[bc]檔案不存在,因此報錯 -->
Next is noglob closed:
-rw-r--r-- 1 197108 197121 0 四月  6 10:21 /c/Users/Administrator/Desktop/shellGlobStar/src/set/noglob
-rw-r--r-- 1 197108 197121 5 四月  6 10:35 /c/Users/Administrator/Desktop/shellGlobStar/src/set/nogloc 
<!-- noglob與nogloc同時都被匹配出來了 -->

下面再給出一個例子,該例子你可以知道萬用字元與正則表示式的區別:

set -o noglob
#開啟萬用字元
declare path=$(cd `dirname $0`;pwd)
ls -ahl -n $path/noglob$
echo "Next is noglob closed:"
set +o noglob
#關閉萬用字元
ls -ahl -n $path/noglob$

此時windows上列印結果為:

-rw-r--r-- 1 197108 197121 0 四月  6 08:53 '/c/Users/Administrator/Desktop/shellGlobStar/src/set/noglob$'
Next is noglob closed:
-rw-r--r-- 1 197108 197121 0 四月  6 08:53 '/c/Users/Administrator/Desktop/shellGlobStar/src/set/noglob$'

2.shopt命令

2.1 shopt命令分析

(1) set 命令是 POSIX 規範,shopt 不是,如下可以獲取到每一個命令的配置項的個數:

 set -o | wc -l
 shopt | wc -l

(2)set命令有一個SHELLOPTSexportshellshellsetSHELLOPTS傳入配置項,然後通過export同步即可:

set -o noglob
#通過set設定時,SHELLOPTS變數的值會自動同步(所有開啟的選項名用冒號join 成的字串)
export $SHELLOPTS
#這個變數預設並不是環境變數,需要手動 export 一下,然後子程序 Shell 會獲取到這個環境變數的值

在當前 Shell 中打開了 noglob 選項,然後 SHELLOPTS變數的值會自動同步(所有開啟的選項名用冒號 join 成的字串),但這個變數預設並不是環境變數,需要手動 export 一下,然後子程序 Shell 會獲取到這個環境變數的值,解析之後,開啟這些繼承來的選項。

2.2 shopt命令的環境變數

shopt -s cdspell
#-s開啟cd拼寫檢查,-u關閉檢查
export $BASHSHOPTS
#匯出到子程序shell中

$BASHOPTS 變數,它的功能和 SHELLOPTS 一樣,用來把 shopt 命令開啟的選項傳遞給子程序 Shell。注意:shopt提供了一個-o選項,其用於檢視我們通過set設定的那些選項:

shopt -o noglob

2.3 shopt引數使用

(1)nullglob引數

shopt -u nullglob
#關閉shopt行為,其中-s表示啟用指定的shell行為
declare path=$(cd `dirname $0`;pwd)
cd $path/tmp/empty
#cd到emty目錄
for i in *
   do 
    echo "file: $i";
 done
#此時的"*"就是獲取該目錄下所有的檔案了,包括目錄,但是不包括子目錄以及子目錄下檔案
#注意:此時如果再一個empty目錄下執行上面的for,那麼就會列印"*",即將萬用字元作為字元列印!
shopt -s nullglob
#啟動nullglob,表示沒有匹配的檔案時候直接輸出null
for i in *
 do
  echo "file: $i"; 
done;
#因為我們設定了nullglob,表示如果沒有檔案那麼直接返回null,所以這裡沒輸出任何內容

你可以檢視該庫下面的檔案

(2)failglob引數

shopt -u failglob
#關閉shopt行為,其中-s表示啟用指定的shell行為
declare path=$(cd `dirname $0`;pwd)
cd $path/tmp/empty
#cd到emty目錄
for i in *
   do 
    echo "file: $i";
 done
#此時的"*"就是獲取該目錄下所有的檔案了,包括目錄,但是不包括子目錄以及子目錄下檔案
#注意:此時如果再一個empty目錄下執行上面的for,那麼就會列印"*",即將萬用字元作為字元列印!
shopt -s failglob
#啟動nullglob,表示沒有匹配的檔案時候直接輸出null
for i in *
 do
  echo "file: $i"; 
done;

此時輸出內容為:

$ /c/Users/Administrator/Desktop/shellGlobStar/src/shopt/shopt-failglob.sh
file: *
/c/Users/Administrator/Desktop/shellGlobStar/src/shopt/shopt-failglob.sh: line 14: no match: *
#報錯

也就是說如果我們禁止了failglob,那麼沒有檔案的時候我們列印萬用字元。而如果啟用這個配置,那麼沒有檔案被搜尋到的時候就報錯~

(3)extglob引數

例子1:

shopt -u extglob   
#關閉shopt行為,其中-s表示啟用指定的shell行為
declare path=$(cd `dirname $0`;pwd)
mv !(backup|tmp) backup/

我們關閉了extglob行為,所以我們最後會得到下面的錯誤資訊,也就是我們這裡的擴充套件萬用字元無效:

/c/Users/Administrator/Desktop/shellGlobStar/src/shopt/shopt-extglob.sh: line 4: syntax error near unexpected token `('/c/Users/Administrator/Desktop/shellGlobStar/src/shopt/shopt-extglob.sh: line 4: `mv !(backup|tmp) backup/')`

正確的例子如下:

shopt -s extglob   
#啟動shopt行為,其中-s表示啟用指定的shell行為
declare path=$(cd `dirname $0`;pwd)
mv !(backup|tmp) backup/

此時你會發現除了tmp/backup目錄外,其它的檔案全部被移動到backup/目錄下。

例子2:rm -rf刪除檔案

shopt -s extglob   
#啟動shopt行為,其中-s表示啟用指定的shell行為
rm -rf file[1-2].js

例子3:擴充套件模式之*(pattern-list)

shopt -s extglob
for i in *(f)ile1.js
 do 
   echo "FileName:$i";
 done

此時你會發現列印的結果如下:

FileName:fffile1.js
FileName:ffile1.js
FileName:file1.js
FileName:ile1.js
//也就表示f這個字元出現0此或者1-無窮多次

例子4:擴充套件模式之+(pattern-list)

shopt -s extglob
for i in +(f)ile1.js
 do 
   echo "FileName:$i";
 done

此時你們看到的結果將會是如下的內容:

FileName:fffile1.js
FileName:ffile1.js
FileName:file1.js

也就是說,此種匹配模式會匹配f字元的>=1次!

例子6:擴充套件模式之?(pattern-list)

shopt -s extglob
for i in ?(f)ile1.js
 do 
   echo "FileName:$i";
 done

此時結果如下:

FileName:file1.js
FileName:ile1.js

f字元出現次數為0|1

例子7:匹配模式@(pattern-list)

shopt -s extglob
echo 'Shell本身的PID'$$
#Shell本身的PID(ProcessID) 
echo 'Shell最後執行的後臺process的PID:'$!
#Shell最後執行的後臺process的PID:
echo "所有引數列表"$* 
#shopt-extglob.sh win-98 sex name執行shell程式得到的結果為:win-98 sex name
echo "引數個數:"$#
#新增到shell中引數個數
echo "shell本身檔名" $0
# @(pattern-list) 表示匹配列表中其中一個pattern
echo "逐個訪問引數"$1
#新增到Shell的各引數值。$1是第1引數、$2是第2引數…
# !(pattern-list) 表示匹配任何一個非列表中的pattern
case $1 in
@(win-98|win-xp|win-7|win-10))
   echo "windows";;
@(Redhat*|Centos*|Debian*|Ubuntu*))
  echo "linux";;
*) 
echo "others";;
esac

也就是我們的模式滿足一個就可以了

例子7:!(pattern-list)

shopt -s extglob
case $1 in 
    !(windows7|windowXP))
      echo "You are not in windows7 nor windowXP platform";;
     *)
#This is default router
     echo "You are  in windows  platform";;
esac

這種匹配和正則模式一致!

(4)dotglob引數

shopt -u dotglob 
#檔案開頭是"."也能匹配
for i in *
 do
    echo 'Filename:'$i
 done

如果設定了這個引數那麼那些檔名以‘.’開頭的檔案也能匹配,結果如下:

Filename:..dotfile
Filename:.dotfile
Filename:backup
Filename:fffile1.js

(5)globstar選項

function show()
{
        for i in **
        # * 被展開成當前目錄下所有檔名
        # ** 當前目錄以及子目錄下的所有的檔案
        # **就是我們常說的globalStar
        do
                echo FileName is $i
                # 前面不需要新增引號表示字串
        done
}

cd  ../../src/
echo "------------------------"
echo "disable globstar option:"
# globstar is disabled by default
shopt -u globstar
show
echo "------------------------"
echo "enable globstar option:"
shopt -s globstar
show

此時你會看到下面的結果(擷取部分內容):

------------------------
disable globstar option:
FileName is find
FileName is outer
FileName is set
FileName is shopt
------------------------
enable globstar option:
FileName is find
FileName is find/find-regex.sh
FileName is outer
FileName is outer/inner
FileName is outer/inner/inner.txt
FileName is outer/outer.txt
FileName is set
FileName is set/file.txt
FileName is set/file1.txt
FileName is set/file2.txt

總之,開啟了globstar,那麼我們會遍歷資料夾下的所有的檔案以及子檔案!

(6)其他配置如nocaseglob

如果開啟了該引數,那麼我們不會對檔名大小寫敏感了!

3.find命令

find支援檔名的正則表示式查詢,按檔案修改時間查詢,按檔案大小查詢,按檔案許可權查詢,按檔案型別查詢等,查詢到以後還支援直接對查詢到的檔案使用命令,功能非常強大。

3.1 正則表示式查詢

典型的find命令的寫法是:

find 查詢路徑 查詢的標準 查詢到之後的動作

比如: find /home -type d -ls意思是: 找出/home/下所有的目錄,並顯示目錄的詳細資訊。下面給出一個例子:

declare currentFolder=$(cd `dirname $0`;pwd)
#$0表示當前shell檔案
#到當前目錄中
# filelist=$(find $currentFolder -regex .*.sh)
filelist=`find $currentFolder -regex .*.sh`
echo "regex檔案查詢結果:"$filelist 

這個是查詢shell檔案所在的目錄的所有的.sh的shell檔案。注意:使用-regex時有一點要注意:-regex不是匹配檔名,而是匹配完整的檔名(包括路徑)。例如,當前目錄下有一個檔案”abar9”,如果你用”ab.9”來匹配,將查詢不到任何結果,正確的方法是使用”.*ab.*9”或者”./ab.*9”來匹配。同時,這個例子也展示瞭如何將find命令返回的結果儲存到變數中,可以通過反引號“也可以通過$(command)將命令包裹起來

3.2名稱查詢-name/-iname

declare currentFolder=$(cd `dirname $0`;pwd)
#$0表示當前shell檔案
#到當前目錄中
# filelist=$(find $currentFolder -regex .*.sh)
filelist=$(find ./ -name "[a-z]*.sh")
#注意:-name/-iname都是使用萬用字元而不是正則表示式來匹配檔名的,如果使用find ./ -name ".*\.sh$" -ls
#得不到任何內容
echo "name檔案查詢結果:"$filelist 

一定要注意,使用-name或者-iname是萬用字元而不是正則表示式!而且最好將萬用字元使用引號擴起來

3.3使用type查詢

declare currentFolder=$(cd `dirname $0`;pwd)
#$0表示當前shell檔案
#到當前目錄中
# filelist=$(find $currentFolder -regex .*.sh)
filelist=$(find ./ -type f)
#注意:-name/-iname都是使用萬用字元而不是正則表示式來匹配檔名的,如果使用find ./ -name ".*\.sh$" -ls
#得不到任何內容
echo "type檔案查詢結果:"$filelist 

-type 檔案屬性如下:

d: 目錄
f: 普通檔案
l: 連結檔案(link)
s: socket檔案
p: 管道檔案(pipe)
b: 塊裝置檔案
c: 字元裝置檔案

3.4 uid/gid/user等查詢

declare currentFolder=$(cd `dirname $0`;pwd)
#$0表示當前shell檔案
#到當前目錄中
# filelist=$(find $currentFolder -regex .*.sh)
filelist=$(find $currentFolder -user administrator)
echo "uid/gid/user檔案查詢結果:"$filelist 

可以查詢的引數如下:

 -gid GID
-uid UID
-user USER
-group GROUP
-nouser
-nogroup

注意:此時獲取到的是檔案的絕對路徑

3.5檔案大小查詢

declare currentFolder=$(cd `dirname $0`;pwd)
#$0表示當前shell檔案
filelist=$(find $currentFolder -type f -size -3k)
#注意:獲取size小於3k的檔案,如果沒有單位那麼1=0.5k!
echo "size檔案查詢結果:"$filelist 

注意:預設單位是b,而它代表的是512位元組,所以2表示1K,1M則是2048,如果不想自己轉換,可以使用其他單位,如c、K、M等。

3.5 ctime,atime,mtime查詢

mtime(modified time):僅僅在檔案內容發生變化以後才會改變,而如檔名或者檔案的屬性發生修改了值是不變的!

ctime(change time):當檔案內容或者檔名檔案屬性發生變化以後,該值都會發生變化!

在通過ls命令可以輸出上面三種時間:

ls -lc filename 列出檔案的 ctime
ls -lu filename 列出檔案的 atime
ls -l filename 列出檔案的 mtime

下面也給出一個mtime的例子:

-mtime 0 
//表示檔案修改時間距離當前為0天的檔案,即距離當前時間不到1天(24小時)以內的檔案
-mtime -1
//表示檔案修改時間為小於1天的檔案,即距離當前時間1天(24小時)之內的檔案
-mtime 1
//表示檔案修改時間距離當前為1天的檔案,即距離當前時間1天(24小時-48小時)的檔案
-mtime+1 
//表示檔案修改時間為大於1天的檔案,即距離當前時間2天(48小時)之外的檔案。這裡-mtime +1 為什麼是48小時以外,而不是24小時以外呢,因為n只能是整數,比1大的下一個整數是2,所以是48小時以外的

下面是一個shell例項:

declare currentFolder=$(cd `dirname $0`;pwd)
#$0表示當前shell檔案
ctime=$(find $currentFolder -cmin +10 -ls )
#顯示當前目錄下所有超過[10min]沒有改變過的檔案的詳細資訊,包括檔名或者檔案屬性
atime=$(find $currentFolder -atime -1 )
#顯示當前目錄下所有在1天以內訪問過得檔案的詳細資訊 
mtime=$(find $currentFolder -mtime 0)
#顯示修改時間在[0,24h]之內的檔案,檔案內容沒有變化
echo "10分鐘內沒有修改的檔案:"$ctime 
echo "1天以內沒有訪問的檔案:"$atime
echo "24小時之內沒有修改的檔案:"$mtime

3.6 許可權查詢

基本說明如下:

-perm MODE 
/MODE: 任意一位匹配即滿足條件 
-MODE: 檔案許可權能完全包含此MODE時才符合條件

下面是一個簡單的例子:

find . -perm -644 -ls 
#顯示當前目錄下檔案許可權的每一位至少包含r-xr–r–的檔案的詳細資訊 
find . -perm /464 -ls 
#顯示當前目錄下檔案許可權的某一位至少包含r–rx-r–的檔案的詳細資訊

3.7組合查詢

declare currentFolder=$(cd `dirname $0`;pwd)
#$0表示當前shell檔案
filelist=$(find $currentFolder -type f -a -size -3k -a -name "[a-z]*.txt")
#注意:獲取size小於3k的檔案,如果沒有單位那麼1=0.5k!
echo "size檔案查詢結果:"$filelist 

這裡是查詢所有檔案,大小小於3看,同時name以.txt結尾,注意這裡是萬用字元的name查詢,而不是regex~,下面是組合符號:

-a: and 
-o: or 
-not:

3.8 find的後繼操作命令

-print: 顯示
-ls:類似ls -l的形式顯示每一個檔案的詳細
-quit: 查詢到一個就退出
-delete: 刪除匹配到的行
-ok COMMAND {} \; 每一次操作都需要使用者確認,{}表示引用找到的檔案,是佔位符
-exec COMMAND {} \; 每次操作無需確認

下面是shell命令的例子:

declare currentFolder=$(cd `dirname $0`;pwd)
# find . -type d -delete 
#刪除當前目錄下的所有目錄 
find $currentFolder -type f -ok wc -l {} \;
#找出當前目錄下所有的普通檔案並顯示檔案行數(需確認) 
#其中-c表示多少位元組,l表示行數(如果是n就有n+1行,至少我這裡測試的結果是這樣的),-w有多少個英文單詞
#-L列印最長的哪一行的長度
# find . -type f -exec wc -l {} \; 
#找出當前目錄下所有的普通檔案並顯示檔案行數(無需確認)
echo "下面不需要詢問"
find $currentFolder -type f -exec wc -l {} \;

其中find $currentFolder -type f -ok wc -l {} \;在詢問的時候不要直接回車,輸入yes/y即可;同時我們的獲取到的line的個數要注意,如果輸出為n那麼行數為n+1。同時-c表示有多少個位元組!

下面再給出幾個例子:

find ./ -name test -print -exec cat {} \;
#列印檔名同時列印檔案內容

4.xargs命令

xargs命令是給其他命令傳遞引數的一個過濾器,也是組合多個命令的一個工具。它擅長將標準輸入資料轉換成命令列引數,xargs能夠處理管道或者stdin並將其轉換成特定命令的命令引數。xargs也可以將單行或多行文字輸入轉換為其他格式,例如多行變單行,單行變多行。xargs的預設命令是echo,空格是預設定界符。這意味著通過管道傳遞給xargs的輸入將會包含換行和空白,不過通過xargs的處理,換行和空白將被空格取代。xargs是構建單行命令的重要元件之一。

4.1匿名管道 vs 命名管道

使用管道進行通訊時,兩端的程序向管道讀寫資料是通過建立管道時,系統設定的檔案描述符進行的。從本質上說,管道也是一種檔案,但它又和一般的檔案有所不同,可以克服使用檔案進行通訊的兩個問題,這個檔案只存在記憶體中。你可以檢視 linux管道的那點事 。對於管道來說具有以下特點:

* 資料只能由一個程序流向另一個程序(其中一個讀管道,一個寫管道);如果要進行雙工通訊,需要建立兩個管道。

* 管道只能用於父子程序或者兄弟程序間通訊。也就是說管道只能用於具有親緣關係的程序間通訊。

上面是匿名管道的內容,下面我們看看命名管道:

FIFO不同與管道之處在與她提供一個路徑名與之關聯以FIFO的檔案形式儲存在檔案系統中有名管道是一個裝置檔案,因此,即使程序與建立FIFO的程序不存在親緣關係,只要可以訪問該路徑,就能夠通過FIFO相互通訊了。值得注意的是FIFO(First In First Out)總是按照先進先出的原則工作,第一個被寫入的資料首先從管道中讀出。

在Linux中我們經常使用管道重定向資料,如下面的例子:

ls > a.txt

4.2 xargs -i/xargs -I

下面給出一個例子:

 declare path=$(cd `dirname $0`;pwd)
 #當前路徑
 find $path -type f -name "*.txt" | xargs -i cp {}  $path/tmp/k/
 # 加-i 引數直接用 {}就能代替管道之前的標準輸出的內容
 # 前面find命令就是獲取到當前目錄所有的txt檔案,而`{}`就是代表所有的`txt`檔案
 # 通過這個命令可以將這些檔案移動到`tmp/k/`目錄

下面是xargs-I的例子:

 declare path=$(cd `dirname $0`;pwd)
find $path -type f -name "*.txt" | xargs -I [] cp []  $path/tmp/n/
# 加 -I 引數 需要事先指定替換字元,該替換字元就是用於代表前面的管道輸出,即find命令的結果
# 這裡表示使用[]表示find的查詢結果

下面是一個稍微複雜一點的例子:

declare path=$(cd `dirname $0`;pwd)
#當前的工作目錄
cat $path/args.txt | xargs -I {} $path/sk.sh -p {} -l
#  Prompt for confirmation before running each command line.
# Only run the command line if the response starts with 'y' or 'Y'. Implies -t.

此時你會發現xargs後面還接著運行了一個shell命令了,在sk.sh中我們可以看到傳入了三個引數:

echo "所有引數列表"$* 
#有-p,aaa,-l
#-p,bbb,-l
#-p,ccc,-l

下面是xargs與find命令一起使用時候的例子:

declare path=$(cd `dirname $0`;pwd)
find $path -type f -name "*.log" -print0 | xargs -0 rm -f
#print0的作用如下:
#True; print the full file name on the standard output, followed by a null character (instead of the newline character that '-print' uses).
# This allows file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output. This option corresponds to the '-0' option of xargs.This option corresponds to the '-0' option of xargs.
#使用print0輸出完整的檔名,即使檔名中有換行符或者空格也能正確的處理。這個選項與xargs的-0一起使用

下面的例子是xargs壓縮檔案與下載資源的例子:

cat url-list.txt | xargs wget -c
#將url-list.txt中的url全部下載下來
find . -type f -name "*.jpg" -print | xargs tar -czvf images.tar.gz
#將所有的jng打包成為一個檔案

以上幾個例子你也可以檢視xargs命令

4.3 子shell

#!/bin/bash
cd ./tmp/n

此時當你在該目錄下執行該檔案你會發現我們的控制檯的路徑仍然為:

 ~/Desktop/shellGlobStar/src/xargs

如果你要在父shell中執行該指令碼可以如下:

. ./test.sh

此時你會發現,控制檯路徑已經變化為./tmp/n了

參考文獻: