[TOC]
Shell基本介紹
shell學習必備基礎
- Linux的基本使用
- 如何在bash上執行程序
- 簡單的管道傳輸
- 使用 &將程序放在后臺執行
入門
為什么要使用shell腳本?
使用腳本編程語言的好處是,腳本語言多半運行在比編譯語言還高得層級,能夠輕易處理文件與目錄之類的對象.
缺點:一般情況下,效率比較低.不過權衡之下,腳本的執行速度已經很快,快到足以讓人感覺不到性能不高了.
常用的腳本編程語言有:shelll,Ruby,JavaScript等.shell似乎是不同版本的linux系統之間的通用功能.shell腳本只要用心寫,就能應用到很多系統上.
shell腳本的過人之處
簡單性:shell是高級語言
可移植性:通過POSIX(可移植操作系統接口,是IEEE為要在各種UNIX操作系統上運行的軟件,而定義API的一系列互相關聯的標準的總稱)所定義的功能,可以在不同的系統上執行,無需需改.
開發容易:短時間即可完成一個功能強大又好用的腳本(字啊以后的學習中就能看到)
簡單的shell腳本
who
能看到以下內容
[root/shell] ]$cat 1.sh
#!/bin/bash
who[root/shell] ]$./1.sh
root :0 2016-09-19 13:53 (:0)
root pts/0 2017-02-13 12:49 (:0)
root pts/2 2017-02-19 20:57 (192.168.247.1)
表示系統當前有多少人登陸,登陸用戶-使用的終端-登陸時間&登出時間
who | wc -l
統計當前登陸了多少人
腳本第一行
#/bin/bash
指定運行此腳本的程序
也可以執行一些獨立的程序 比如:#/bin/awk -f
以下是幾個初級的陷阱:
1.對#!這一行的長度盡量不要超過64個字符
2.腳本的可移植性取決于是否有完整的路徑名稱
3.不要在選項之后放置任何空白,因為空白也會跟著選項一起傳遞給被引用的程序.
4.需要知道解釋器的完成路徑的名稱.這樣可以規避可移植性的問題,廠商不同,同樣的東西可能放在不同的地方
5.一些較久的系統,內核不具備#!的能力,有些shell會自行處理,這些shell對于#!與緊隨其后的解釋器名稱之間是否可以有空白,可能有不同的解釋.查看當前發行版本可以使用的shell:cat /etc/shells
查看系統默認的shell:echo $SHELL:一般情況下是輸出/bin/bash.
如果想切換shell的版本,只需要直接輸入shell的版本.例如想使用csh,直接輸入csh即可,使用exit退出當前shell回到原shell.
shell的基本元素
shell識別三種基本命令:
- 內建命令:就是linux的命令,例如cd,ls,mkdir等,這些命令是由于其必要性才內建的,內外一種命令的村子啊是為了效率,其中最典型的就是test,
- shell函數:功能健全的一系列程序代碼,用shell語言寫成,可以像使用命令一樣使用,就是在C++中調用函數.
- 外部命令:是由shell的副本(新的進程)所執行的命令,還是命令.
shell中的變量
在shell中,變量值可以是(通常是)空值,也就是不含有任何字符.這是合理的,也是常見的,好用的特性.空值就是null
在shell中變量名的長度無限制,所能保存的字符數同樣沒有限制.
變量的賦值方式:變量名=值,中間不能有任何的空格,如果想去除shell變量的值時,需要在變量名前加上$字符.當所賦予的值包含空格的時候,需要將值用單引號或者雙引號包起來,用單引號包起來的后果是單引號里面的所有特殊符號都不具備特殊含義,用雙引號包起來代表特殊符號有特殊含義.
例如:
name=syx;
echo 'name' #輸出 name
echo "$name" #輸出 syx
如果想將 name1=syx,name2=zsf合并,成syxzsf
則name=${name1}${name2},echo $name name=syxzsf
,貌似還有其他的合并方法,個人覺得這一種最好.
至于變量的四種類型什么的,暫時不學。
簡單的echo輸出
echo的作用就是產生輸出,可以提示用戶,或者用來產生數據提供用戶,或者產生數據進一步處理.
以前的echo只能將參數打印到shell交互界面上,參數之間以一個空格隔開,并以換行符號結尾.
后來又衍生出了-n選項,省略結尾的換行符號.
etho的語法:
etho [string......]
用途是產生shell腳本的輸出,沒有什么主要選項.行為模式是將參數打印到標準輸出,參數之間用空格隔開,并以換行符結尾.轉義序列用來表示特殊字符,以及控制其行為模式.
參 數:
-n 不要在最后自動換行
-e 若字符串中出現以下字符,則特別加以處理,而不會將它當成一般文字輸出:
常用的轉移序列:
\a #發出警告聲
\b #刪除前一個字符
\c # 最后不加上換行符號;
\f #換行但光標仍舊停留在原來的位置
\n #換行
\r #回車
\t #水平制表符
\v #垂直制表符
\\ #反斜杠字符
華麗的printf 輸出
如同echo命令,printf命令可以輸出簡單的字符串,但是printf 不提供自動換行
語法
printf format-string [arguments...]
printf "Hello, Shell\n"
Hello, Shell
# format-string為雙引號
printf "%d %s\n" 1 "abc"
1 abc
# 單引號與雙引號效果一樣
printf '%d %s\n' 1 "abc"
1 abc
# 沒有引號也可以輸出
printf %s abcdef
abcdef
# 格式只指定了一個參數,但多出的參數仍然會按照該格式輸出,format-string 被重用
printf %s abc def
abcdef
printf "%s\n" abc def
abc
def
printf "%s %s %s\n" a b c d e f g h i j
a b c
d e f
g h i
j
# 如果沒有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
and 0
# 如果以 %d 的格式來顯示字符串,那么會有警告,提示無效的數字,此時默認置為 0
printf "The first program always prints'%s,%d\n'" Hello Shell
-bash: printf: Shell: invalid number
The first program always prints 'Hello,0'
I/O重定向
標準的輸入輸出
在了解重定向之前,需要先了解一下標準的輸入輸出,總的來說,所有的數據都有來源,也都應該都重點,默認的標準輸入輸出就是終端.
例如:
我們只是輸入 cat命令,并不指定任何參數,接著我們輸入hello world,就是打印helloworld 到終端.
[root~] ]$cat
test #輸入的
test #輸出的
所謂的I/O重定向就是通過與終端交互,或是在shell腳本里設置,重新安排從哪里輸入或者輸出到哪里.
重定向與管道
< 改變標準輸入
> 改變標準輸出
重定向符號在目的地文件不存在的時候會新建一個文件,如果存在,則會覆蓋!
>> 此重定向符號為追加
| 管道可以將 標準輸出 改為標準輸入 如 cat /etc/paswd | wc -l
在構造管道的時候,應該試著讓每個階段的數據量變少,也就是說,把會讓數據變少的命令放在前邊,提高后面的命令的執行效率.例如,sort之前,先用grep找出相關的行,這樣可以讓sort少做些事.
/dev/null和/dev/tty
/dev/null
當被用作重定向輸出時,程序的輸出被直接丟棄。該文件用在那些不關心程序輸出的地方。
當被用作重定向輸入時,輸入則是文件結束。
/dev/tty
當被用作重定向時,表示重定向到終端。
shell基本命令查找與添加
shell會沿著$PATH來尋找命令.$PATH是一個以冒號分割的目錄列表,你可以在列表所指定的目錄下找到所要執行的命令.命令可能是shell腳本,也可能是編譯后的可執行文件,從用戶角度來看,二者并無不同.
默認路徑至少包含/bin和/usr/bin,或許還包含其他的.
名稱為bin的目錄用來保存可執行文件.
如果要編寫自己的腳本,最好準備一個自己的bin目錄來存放他們,并且讓shell能夠自動找到他們.
要想永久生效,在/etc/profile文件中把你的bin目錄加入到$PATH,而每次登陸時Shell都將讀取.profile文件.
PATH=$PATH:[你存放腳本的目錄] 如“$PATH:/usr/local/myshell
$PATH里的空項目表示當前項目.空項目位于路徑中間時,可以用兩個連續的冒號來表示,如果將冒號直接置于最前端或尾端,分別表示查找的時候最先查找或最后查找當前目錄.
$PATH=:/bin:/usr/bin 先找當前目錄
$PATH=/bin::/usr/bin 當前目錄居中
$PATH=/bin:/usr/bin: 最后找當前目錄
不應該在查找路徑中放進當前項目.
訪問腳本的參數
比如我們想查看當前某個用戶是否登陸,那么新建腳本 finduser.sh
#!/bin/bash
who | grep $1
./finduser/sh root
$1 就是指執行腳本傳入的第一個參數 root
root :0 2016-09-19 13:53 (:0)
root pts/0 2017-02-13 12:49 (:0)
root pts/2 2017-02-20 20:45 (192.168.247.1)
理想狀態下,是這樣的,但是如果用戶不輸入參數,腳本就會報錯了,所以更好的情況是把腳本可能出現的事件都做個判斷以及友情的提示,更加智能化
基本的正則表達式(BRE)
簡單的正則舉例
tolstoy: 匹配一行上任意位置的7個字母:tolstoy
^tolstoy: 7個字母tolstoy,出現在一行的開頭
tolstoy$: 出現在一行的結尾
^tolstoy$: 正好包含這7個字母的一行,沒有其他的任何字符.
[tT]olstoy: 在一行的任意位居中,含有Tolstoy或者tolstoy
tol.toy:在一行的任意位居中,含有tol這三個字母,加上一個特殊字符,在接著toy這三個字母
tol.*toy:在一行的任意位居中,含有tol這三個字母,加上任意的0或者多個字符,
再繼續toy這三個字母(例如:toltoy,tolstoy,tolWHOtoy都是滿足要求的).
shell中的通配符
*: 代表0個或者多個任意字符
?: 代表一定有一個的任意字符
[]: 代表一定有一個在括號內的字符(非任意字符).例如[abcd]代表一定有一個字符,可能是abcd這四個選項的任意一個.
[-]: 代表在編碼順序內的所有自負.例如:0-9代表0到9之間的所有數字,因為數字的語系編碼是連續的.
[^]: 若括號內的第一個字符為指數字符\(^) 表示反選,例如:\^abc代表是非abc的其他字符就可以.
shell中的特殊字符
#: 注釋字符
\: 將特殊字符或者通配符還原成一般字符
|: 管道符,分割兩個管線命令的界定
;: 連續命令下達分隔符
~: 用戶的家目錄
$: 放在變量前面,正確使用變量
&: 工作控制,將命令編程背景下工作
!: 非(!)的意思,邏輯運算符
>,>>: 輸出重定向,分別是覆蓋和追加
><,<<: 輸入重定向
>'': 單引號,不具有變量置換的功能
>"": 雙引號,具有變量置換的功能
>(): 在中間的為子shell的起始與結束
>{]: 在中間為命令塊的組合
shell中正則表達式的控制字符
^: 匹配行首位置
$: 匹配行尾位置
.: 匹配任意祖父
*: 對*之前的匹配整體或字符匹配任意次(包括0次)
\?: 對\?之前的匹配整體或字符匹配0次或1次
\{n\}: 對 \ { 之前的匹配整體或字符匹配n次
\{m,\}: 對 \ { 之前的匹配整體或字符匹配至少m次
\{m,n}: 對 \ { 之前的匹配整體或字符匹配m到n次
[abcdef]: 對單字符而言匹配中的字符
[a-z]; 對單字符而言,匹配任意一個小寫字母
[^a-z]: 不匹配括號中的內容
基本正則表達式
匹配單個字符
1.匹配一般字符:一般字符是指無特殊含義的字符,包括所有文本和數字字符,絕大多數的空白字符以及標點符號字符,因此,正則a,匹配a.
2.如果相匹配,因為是特殊字符,所以需要用 \ 轉義,正則*,匹配*.
3..(點號)字符意即”任意字符”,例如a.c匹配于abc,aac.
4.使用方括號表達式.例如x[abcdefg]z,可以匹配xaz,xbz,等,方括號里如果存在(^),表示取反的意思,就是說不匹配列表里的任意字符.
[0123456789]表示所數字,但是這樣寫太麻煩,我們可以用[0-9]來表示,[abcdefg]同樣可以用[a-g]
單個表達式匹配多字符
最簡單的辦法就是把它們一一列出來:正則abc匹配于abc.
雖然(.)meta字符與方括號表達式都提供了依次匹配一個字符的很好方式,單正則真正強大而有力地功能是修飾符meta字符的使用上.
最常用的修飾符是(*),表示匹配0個或多個前面的單個字符.因此ab*c表示”匹配一個a,0個或多個b字符以及a空c”.這個正則匹配的有ac,abc,abbcabbbbc.
匹配0或多個,不表示匹配其他的某一個.例如正則abc,文本aQc是不匹配的.但是ac是匹配的.
()修飾符雖然好用,但是他沒有限制,如要只要指定次數,使用一個復雜的方括號表達式雖然也能指定次數,但是太過麻煩.我們就引入了區間表達式.所謂的區間表達式有三種變化
\{n\}
前置正則表達式所得結果重現n次
\{n,\}
前置正則表達式所得結果至少出現n次
\{n,m\}
出現n到m次
例如我們想要表達”重現5個a' =>a\{5\}'
,重現10到42個q'=>q\{10,42\}'
;
文本匹配錨點
兩個meta字符是脫節符號(^),與貨幣字符($),他們叫做錨點,因為其用途在限制正則表達式匹配時,針對要被匹配字符的開始或者結尾處進行匹配,
假定有一串字符串:abcABCdefDEF
模式 | 是否匹配 | 理由 |
---|---|---|
ABC | 是 | 居中的456匹配 |
^ABC | 否 | 起始不是ABC |
def | 是 | 居中的def匹配 |
def$ | 否 | 結尾不是def |
[[:upper:]]{3} | 是 | 結尾的大寫ABC匹配 |
[[:upper:]]{3}$ | 是 | 結尾的大寫DEF匹配 |
^[[:alpha:]]{3} | 是 | 結尾的大寫ABC匹配 |
BRE運算優先級,由高到低
[..]\[==][::] -----用于字符拍的方括號符號
\metacharacter ----轉移的meta字符
[] --------------- 方括號表達式
\{ \} ------------子表達式
* \{ \ } ---------前置單個字符重現的正則表達式
無符號--------------連續
^$ -------------- 錨點
擴展正則表達式(ERE)
BRE與ERE在大多數的meta字符與功能應用上幾乎是完全一致,單ERE理由寫meta字符看起來與BRE類似,卻具有完全不同的類型.
擴展正則表達式與基礎正則表達式的唯一區別在于:? + () {} 這幾個字符。
基礎正則表達式中,如果你想? + () {}表示特殊含義,你需要將他們轉義
而擴展正則表達式中,如果你想? + () {} 不表示特殊含義,你需要將他們轉義。
轉義符號,都是一樣的,\符號。
所謂特殊含義,就是正則表達式中的含義。非特殊含義,就是這個符號本身。
例如
echo aaa|grep 'a?';
echo aaa|grep 'a\?'; \#aaa
\#egrep使用的是擴展正則表達式
echo aaa|egrep 'a?'; \#aaa
echo aaa|egrep 'a\?';
打印
如果希望打印文件,最好預先處理一下,包括調整邊距,設置行高,設置標題等,這樣打印出來的文件更加美觀.當然,不處理也能打印,但是可能會比較丑陋.
pr命令
pr命令就是轉換文件格式的,可以把較大的文件分割成多個頁面進行打印,并未每個頁面添加標題.
語法:
pr option(s) filename(s)
常見選項:
> -k:分成激烈打印,默認為1
> -d:兩倍行距(并不是所有版本的pr都有效)
> -h “title” 設置每個文件的標題
> -l PAGE_LENGTH :每頁顯示多少行.默認是每個頁面一共66行.
> -o MARGIN:每行縮進的空格數
> -w PAGE_WIDTH:多列輸出時,設置頁面寬度,默認是72個字符.
例如我有一個文件food,里面的內容為:
Sweet Tooth
Bangkok Wok
Mandalay
Afghani Cuisine
Isle of Java
Big Apple Deli
Sushi and Sashimi
Tio Pepe's Peppers
使用命令:pr -2 -h "food" food
輸出結果為:
2015-06-22 12:27 food
weet Tooth Isle of Java
Bangkok Wok Big Apple Deli
Mandalay Sushi and Sashimi
Afghani Cuisine Tio Pepe's Peppers'
解釋:
pr會以文件的修改時間作為頁面標題的時間戳;如果輸入時自管道而來,則使用當前的時間,接上文件名稱(如果輸入的數據內容在管道中,則為空)以及頁碼.
lp 和 lpr 命令將文件傳送到打印機進行打印。使用 pr 命令將文件格式化后就可以使用這兩個命令來打印。
例如:
pr -2 -h "food" food | lpr
命令成功執行會返回一個表示打印任務的ID,通過這個ID可以取消打印或者查看打印狀態。
如果你希望打印多份文件,可以使用 lp 的 -nNum 選項,或者 lpr 命令的 -Num 選項。Num 是一個數字,可以隨意設置。
如果系統連接了多臺打印機,可以使用 lp 命令的 -dprinter 選項,或者 lpr 命令的 -Pprinter 選項來選擇打印機。printer 為打印機名稱。
lpstat 和 lpq 命令
lpstat 命令可以查看打印機的緩存隊列(有多少個文件等待打印),包括任務ID、所有者、文件大小、請求時間和請求狀態。提示:等待打印的文件會被放到打印機的的緩存隊列中。
使用 lpstat -o 命令查看打印機中所有等待打印的文件,lpstat -o 命令按照打印順序輸出隊列中的文件。
cancel 和 lprm 分別用來終止 lp 和 lpr 的打印請求。使用這兩個命令,需要指定ID(由 lp 或 lpq 返回)或打印機名稱。
lprm 命令用來取消當前用戶的正在等待打印的文件,使用任務號作為參數可以取消指定文件,使用橫線(-)作為參數可以取消所有文件。
lprm 會返回被取消的文件名。
提取文件開頭或者結尾
head & tail
個人覺得最好用的顯示文本文件的頭幾行最好用的是 head -n [file(s)]
head的常用選項:
> -q: 隱藏文件名
> -v: 顯示文件名
> -c<字節>: 顯示字節數
> -n<行數>: 顯式的行數
在交互式shell通信期中,有時需要監控某個文件的輸出----如日志這類持續寫入狀態的文件.-f選項這時就派上用場了,他可以要求tail顯示指定的文件結尾行數,接著進入無止境的循環中----休息一秒后又再度醒來并檢查是否需要顯示更多的輸出結果.再設置-f的狀態下,tail只有當你中斷它時才會停止----通常是輸入Ctrl+C來中斷;
tail -n 25 -f /var/log/messages
此選項不可用于shell腳本.
直到按了ctrl+c選項后才停止.
由于tail加上-f選項之后便不會自己中斷,所以此選項不能用于shell腳本.使用-f選項有實時監聽的效果.
head案例:
> 使用命令:head -n 3 /etc/passwd結果是顯示文件的頭三行,
> 如果命令為:head -n -3 /etc/passwd 結果是顯示除了最后三行都顯示,注意到區別沒有?
> 相似的,顯示文件的前n個字節,以及除了最后n個字節以外的內容也沒問題了.
> head和tail如果組合使用:
> head -n 5 /etc/passwd | tail -n 3
> 輸出/etc/passwd的第三道第五行.
shell變量與算數
test.sh
name=vic #定義一個變量
readonly name #變量只讀
unset name #刪除變量
sleep 5 #等待5秒
參數的展開
var="hello world"
echo ${var}
#hello world
#所謂的參數的展開,基本上就是變量,shell中,參數是變量的超集,只不過變量不能以數字開頭,而參數可以,比如$1,表示傳遞的第一個參數
位置參數
$0 #程序名
$1到$9 #直接表示
${10} #大于9的時候要用花括號
$* #接收所有參數,
$@ #傳遞給腳本或函數的所有參數。被雙引號(" ")包含時,與 $* 稍有不同
#但是當它們被雙引號(" ")包含時,"$*" 會將所有的參數作為一個整體!以"$1 $2 … $n"的形式輸出所有參數;"$@" 會將各個參數分開,以"$1" "$2" … "$n" 的形式輸出所有參數。
$? #上個命令的退出狀態,或函數的返回值
$$ #SHELL的進程ID
$# #參數個數
展開運算符(參數的運算符)
${varname:-zili}
#如果varname存在且不是NULL,返回varname,反之,設為zili。可用于設置變量默認值!!
${varname:+zili}
#如果varname存在且不是NULL,返回varname,反之,返回null
模式運算符
var=br1br2ead
echo ${var$$*br}
輸出:2ead
echo ${var#*br}
輸出:1br2ead
${parameter%word}或${parameter%%word}
作用:與前例相似,唯一不同的是從$parameter的為不開始匹配.
var="La.Maison.en.Petits.Cubes.avi"
echo ${var%.*}
輸出:La.Maison.en.Petits.Cubes
echo ${var%%.*}
輸出:La
分析:匹配案例中的”.”時,shell會從$var的尾部開始查找”.”,如果是最短匹配(echo ${var%.*}),則會找到第一個”.”就停止,否則(echo ${var%%.*})會一直找到最后一個”.”才停止.可以看到,這種用法可以分方便的去掉文件后綴,從而得到文件名.
退出狀態和 if 語句
退出狀態
每一條命令,不管是內置的,shell函數,還是外部的,當它退出時,都會返回一個小的整數值給引用它的程序,這就是程序的退出狀態.在shell下執行進程有很多方式可取用程序的退出狀態.
以管理狀態來說,0 表示成功,也就是當$? 返回的值為0的時候,證明程序執行成功,其他為失敗
0 #命令成功地退出
>0 #在重定向或單詞展開期間(~,變量,命令,算數展開,以及單詞切割)失敗
1-125 #命令不成功的退出.特定的退出值的含義,是由各個單獨的命令定義的.
126 #命令找到了,但文件無法執行
127 #命令沒找到
>128 #命令因受到信號而死亡
exit
cd $(dirname $0) || exit 1
##進入腳本所在目錄 否則退出
if [ $# -ne "2" ];
then
echo ''
exit 2
fi
if語法
if [xxxx];then
xxxx
fi
-------------------------
if [xxxx];then
xxxx
else
xxxx
fi
-------------------------
if [xxxx];then
xxxx
elif [xxxx];then
xxxx
....
....
else
xxxx
fi
#!/bin/bash
read -p "what is your backup directoy : " BakDir
if [ -d $BakDir ];then
echo "$BakDir alerdy exist"
else
echo "$BakDir is not exist,will make it"
mkdir $BakDir
fi
#!/bin/bash
UserNum=`who | wc -l`
if [ $UserNum -gt 3 ];
then
echo "Alert, too many login users ( Total: $UserNum)."
else
echo "Login Users:"
who | awk '{print $1,$2}'
fi
注意
if 與[ 之間必須有空格
[ ]與判斷條件之間也必須有空格
]與; 之間不能有空格
邏輯判斷
字符串判斷
str1 = str2 當兩個串有相同內容、長度時為真
str1 != str2 當串str1和str2不等時為真
-n str1 當串的長度大于0時為真(串非空)
-z str1 當串的長度為0時為真(空串)
數字的判斷
int1 -eq int2 兩數相等為真
int1 -ne int2 兩數不等為真
int1 -gt int2int1大于int2為真
int1 -ge int2int1大于等于int2為真
int1 -lt int2int1小于int2為真
int1 -le int2int1小于等于int2為真
文件的判斷
-r file 用戶可讀為真
-w file 用戶可寫為真
-x file 用戶可執行為真
-b file 若文件存在且是一個塊特殊文件,則為真
-c file 若文件存在且是一個字符特殊文件,則為真
-d file 若文件存在且是一個目錄,則為真
-e file 若文件存在,則為真
-f file 若文件存在且是一個規則文件,則為真
-g file 若文件存在且設置了SGID位的值,則為真
-h file 若文件存在且為一個符合鏈接,則為真
-k file 若文件存在且設置了"sticky"位的值
-p file 若文件存在且為一已命名管道,則為真
-s file 若文件存在且其大小大于零,則為真
-u file 若文件存在且設置了SUID位,則為真
-o file 若文件存在且被有效用戶ID所擁有,則為真
復雜的邏輯判斷
-a 與
-o 或
! 非
test的使用
#!/bin/bash
cd /bin
if test -e ./bash //其實這里相當于if [ -e ./bahs ]
then
echo 'the file already exist!'
else
echo 'the file not exist!'
fi
輸出結果為:the file already exist!
注意和簡寫與擴展
注意
if [ -n "$str" -a -f "$file" ] 一個test命令,兩種條件
if [-n "str"] && [ -f "$file" ] 兩個命令,一塊接方式計算
if [-n "$str" && -f "$file"] 語法錯誤!!!
簡寫
[1 eq1 ] &&echo'OK'
輸出:ok
[ 2 < 1 ] &&echo 'OK'
輸出:-bash: 1: No such file or directory
使用命令:[ 2 \< 1 ] &&echo 'OK'這樣就可以了
使用命令:[ 2 -gt 1 -a 3 -lt 4 ]&&echo 'Ok'
輸出:Ok
使用命令:[ 2 -gt 1 && 3 -lt 4 ]&&echo 'Ok'
輸出:-bash: [: missing `]'
注意:在[] 表達式中,常見的>,<需要加轉義字符,表示字符串大小比較,以acill碼位置作為比較。不直接支持<>運算符,還有邏輯運算符 || 和 && 它需要用-a[and] –o[or]表示。
擴展 [[]]
[[ 2 < 3 ]]&&echo 'OK'
輸出OK.
[[ 2 < 3 && 4 < 5 ]] && echo 'ok'
輸出:ok
注意:[[]] 運算符只是[]運算符的擴充。能夠支持<,>符號運算不需要轉義符,它還是以字符串比較大小。里面支持邏輯運算符 || 和 && bash 的條件表達式中有三個幾乎等效的符號和命令:test,[]和[[]]。通常,大家習慣用if [];then這樣的形式。而[[]]的出現,根據ABS所說,是為了兼容><之類的運算符。
不考慮對低版本bash和對sh的兼容的情況下,用[[]]是兼容性強,而且性能比較快,在做條件運算時候,可以使用該運算符。
case
#!/bin/sh
read -p 'input a number 1 to 4' unum
case $unum in
1) echo 'Number is 1'
;;
2|3) echo 'Number is 2 or 3'
;;
4) echo 'Number is 4'
;;
*) echo '1 to 4,Please'
;;
esac
) 相當于其他語言中的default。
除了)模式,各個分支中;;是必須的,;;相當于其他語言中的break
| 分割多個模式,相當于or
循環
for
for循環是將串行的元素取出,依次放入指定的變量中,重復執行在do和done之間的命令,直到所有元素取盡為止
for 變量 in 串行
do
....
done
for((i=0;i<100;i++))
do
....
done
#!/bin/sh
for i in $(seq 1 10)
do
mkdir /tmp/test${i}
cd /tmp/test${i}
for j in $(seq 1 5)
do
touch test${j}
done
done
#!/bin/sh
dir="/var"
cd $dir
for i in $(ls $dir)
do
if [ -d $i ];then
du -sh $i
fi
done
while
while 條件測試
do
....
done
#!/bin/sh
sum=100
i=0
while [ $i -le 100 ]
do
sum=$(($sum+$i))
i=$(($i+1))
done
echo $sum
until
until 條件測試
do
....
done
#條件為真時,循環停止
sum=0
i=1
until ((i>100))
do
sum=$(( $sum+$i ))
i=$(( $i+1 ))
done
echo $sum
退出/跳出循環
break
直接打斷循環
continue
跳過本次循環
shift
shift每執行一次,參數指針像右移動一位,
#!/bin/bash
until [ $# -eq 0 ]
do
echo "第一個參數為: $1 參數個數為: $#"
shift
don
第一個參數為: 1 參數個數為: 4
第一個參數為: 2 參數個數為: 3
第一個參數為: 3 參數個數為: 2
第一個參數為: 4 參數個數為: 1
getopts
以后研究
Linux相關命令詳解
grep
語法
grep (option) [file]
選項
-A 1 表示找到所有匹配行,并顯示所有匹配行后的一行
-B 1 表示找到所有匹配行,并顯示所有匹配行的前面一行
-C 1表示找到所有匹配行,并顯示所有匹配行的前一行,后一行
-V:顯示不匹配的行
-a 表示把所有文件當作ASCII文件來處理 搜索二進制文件
-b 表示顯示match的字符串在文件中的offset
-c 顯示有多少行match
-e 后面跟一個正則表達式,指定多個正則表達式的時候很有用
-f可以指定pattern在我們的文件中 pattern文件中的每一行都會來進行匹配
-i:模式匹配時忽略大小寫
-l 出匹配模式的文件名稱,而不是打印匹配的行
-m 最多匹配幾個后,就停止,這樣速度會比較快
-n 匹配之后,在前面打印行號,這個還是有用的
-o 只打印匹配的內容
-R 搜索子目錄
參數
文件,文檔等
實例
grep -n 'root' /etc/passwd
grep -m 1 'root' /etc/passwd
tr
語法
tr (選項) (參數)
選項
-c或——complerment:取代所有不屬于第一字符集的字符;
-d或——delete:刪除所有屬于第一字符集的字符;
-s或——squeeze-repeats:把連續重復的字符以單獨一個字符表示;
-t或——truncate-set1:先刪除第一字符集較第二字符集多出的字符。
參數
字符集1:指定要轉換或刪除的原字符集。當執行轉換操作時,必須使用參數“字符集2”指定轉換的目標字符集。但執行刪除操作時,不需要參數“字符集2”;
字符集2:指定要轉換成的目標字符集。
實例
輸入的字符串大寫轉成小寫:
echo "HELLO WORLD" | tr 'A-Z' 'a-z'
hello world
#由于是轉換,所以必須要有字符集2 也就是'a-z'
用tr壓縮字符,可以壓縮輸入中重復的字符:
echo "thissss is a text linnnnnnne." | tr -s ' sn'
this is a text line.
sed
語法
sed的命令格式: sed [option] 'sed command'filename
sed的腳本格式:sed [option] -f 'sed script'filename
選項
-n :只打印模式匹配的行
-e :直接在命令行模式上進行sed動作編輯,此為默認選項
-f :將sed的動作寫在一個文件內,用–f filename 執行filename內的sed動作
-r :支持擴展表達式
-i :直接修改文件內容
實例
sed -n '5p' /etc/passwd #打印某一行
sed -n '5,8P' /etc/passwd #打印5-8行
sed -n '/root/p' /etc/passwd #匹配某字符的行
sed -n '/root/,5p' /etc/passwd #以匹配某字符的行 到某行
sed -n '1,/adm/p' /etc/passwd #匹配從哪里開始 以某關鍵字結尾的行
sed -n '1,4{=;p}' /etc/passwd #打印帶行號的
sed -n '1,4!{=;p}' /etc/passwd # !感嘆號取反 不打印1-4行
sed 匹配正則 -r
####### 選項
^ 錨點行首的符合條件的內容,用法格式"^pattern"
$ 錨點行首的符合條件的內容,用法格式"pattern$"
^$ 空白行
. 匹配任意單個字符
* 匹配緊挨在前面的字符任意次(0,1,多次)
.* 匹配任意長度的任意字符
? 匹配緊挨在前面的字符0次或1次
\{m,n\} 匹配其前面的字符至少m次,至多n次
\{m,\} 匹配其前面的字符至少m次
\{m\} 精確匹配前面的m次\{0,n\}:0到n次
\< 錨點詞首----相當于 \b,用法格式:\<pattern
\> 錨點詞尾, 用法格式:\>pattern
\<pattern\> 單詞錨點
分組,用法格式:pattern,引用\1,\2
[] 匹配指定范圍內的任意單個字符
[^] 匹配指定范圍外的任意單個字符
[:digit:] 所有數字, 相當于0-9, [0-9]---> [[:digit:]]
[:lower:] 所有的小寫字母
#######實例
#######sed的匹配模式支持正則表達式#####################
sed '5 q' /etc/passwd #打印前5行
sed -n '/r*t/p' /etc/passwd #打印匹配r有0個或者多個,后接一個t字符的行
sed -n '/.r.*/p' /etc/passwd #打印匹配有r的行并且r后面跟任意字符
sed -n '/o*/p' /etc/passwd #打印o字符重復任意次
sed -n '/o\{1,\}/p' /etc/passwd #打印o字重復出現一次以上
sed -n '/o\{1,3\}/p' /etc/passwd #打印o字重復出現一次到三次之間以上
sed編輯命令
p 打印匹配行(和-n選項一起合用)
= 顯示文件行號
a\ 在定位行號后附加新文本信息
i\ 在定位行號前插入新文本信息
d 刪除定位行
c\ 用新文本替換定位文本
w filename 寫文本到一個文件,類似輸出重定向 >
r filename 從另一個文件中讀文本,類似輸入重定向 <
s 使用替換模式替換相應模式
q 第一個模式匹配完成后退出或立即退出
l 顯示與八進制ACSII代碼等價的控制符
{} 在定位行執行的命令組,用分號隔開
n 從另一個文件中讀文本下一行,并從下一條命令而不是第一條命令開始對其的處理
N 在數據流中添加下一行以創建用于處理的多行組
g 將模式2粘貼到/pattern n/
y 傳送字符,替換單個字符
####### 實例
sed -n '/^#/p' /etc/profile #打印以井號開頭的 '/^#/!P'加感嘆號表示沒有注釋的行
sed -n '/^#/!{/^$/!p}' /etc/profile #這樣過濾所有的#開頭和空白行
sed -e '/^#/d' -e '/^$/d' /etc/profile #等同上面,-e 支持對單個文件進行不同的操作
sed '/root/s/^/SSSSSSSSSSSSS/' /etc/passwd #匹配root的行,行首添加SSSSSS...
sed '/root/s/$/SSSSSSSSSSSSS/' /etc/passwd #匹配root的行,行尾添加SSSSSS...
sed 's/root/& LLL/' /etc/passwd #匹配root關鍵字,前面添加 LLL
sed 's/root/LLL &/' /etc/passwd #匹配root關鍵字,后面添加 LLL
sed '/zabbix/i ZZZ' /etc/passwd #匹配root關鍵字,前一行添加 ZZZ
sed '/zabbix/a ZZZ' /etc/passwd #匹配root關鍵字,后一行添加 ZZZ
sed '/zabbix/a ZZZ \n OOO' /etc/passwd #\n 可以用來換行
sed 's/^/DDDDDDDD/' /etc/passwd #每行的開頭添加 DDDDDD....
sed 's/$/DDDDDDDD/' /etc/passwd #每行的結尾添加 DDDDDD....
sed '1,3s/^/#/' /etc/passwd #每行的開頭# 這個用來添加注釋
sed刪除
sed '/^#/d' #刪除#開頭的
sed '/^#/!d' #刪除非#開頭的
sed '/root/d' #刪除匹配root的字符
sed '/\<you>\/' #刪除包含you這個單詞的行 \<>\用來定位單詞
...
sed替換(腳本中較多使用)
#================源文件里面的內容===============================
[root@jie1 ~]# cat test
anonymous_enable=YES
write_enable=YES
local_umask=022
xferlog_enable=YES
connect_from_port_20=YES
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
DEVICE="eth0"
BOOTPROTO="static"
HWADDR="00:0C:29:90:79:78"
ONBOOT="yes"
IPADDR=172.16.22.1
NETMASK=255.255.0.0
#======================================================================
[root@jie1 ~]# sed -i '/DEVICE/c\Ethernet' test
#匹配DEVICE的行,替換成Ethernet這行
[root@jie1 ~]# sed -i 's/static/dhcp/' test
#把static替換成dhcp(/,@,#都是前面所說的地址定界符)
[root@jie1 ~]# sed -i '/IPADDR/s@22\.1@10.12@' test
#匹配IPADDR的行,把22.1替換成10.12由于.號有特殊意義所有需要轉義
[root@jie1 ~]# sed -i '/connect/s#YES#NO#' test
#匹配connect的行,把YES替換成NO
[root@jie1 ~]# sed -i 's/bin/tom/2g' test
#把所有匹配到bin的行中第二次及第二次之后出現bin替換成tom
[root@jie1 ~]# sed -i 's/daemon/jerry/2p' test
#把所有匹配到bin的行中第二次出現的daemon替換成jerry,并在生產與匹配行同樣的行
[root@jie1 ~]# sed -i 's/adm/boss/2' test
#把所有匹配到adm的行中僅僅只是第二次出現的adm替換成boss
[root@jie1 ~]# sed -i '/root/{s/bash/nologin/;s/0/1/g}' test
#匹配root的行,把bash替換成nologin,且把0替換成1
[root@jie1 ~]# sed -i 's/root/(&)/g' test
#把root用括號括起來,&表示引用前面匹配的字符
[root@jie1 ~]# sed -i 's/BOOTPROTO/#BOOTPROTO/' test
#匹配BOOTPROTO替換成#BOOTPROTO,在配置文件中一般用于注釋某行
[root@jie1 ~]# sed -i 's/ONBOOT/#&/' test
#匹配ONBOOT的行的前面添加#號,在配置文件中也表示注釋某行
[root@jie1 ~]# sed -i '/ONBOOT/s/#//' test
#匹配ONBOOT的行,把#替換成空,即去掉#號,也一般用作去掉#注釋
#================執行以上sed命令之后文件顯示的內容====================
[root@jie1 ~]# cat test
anonymous_enable=YES
write_enable=YES
local_umask=022
xferlog_enable=YES
connect_from_port_20=NO
(root):x:1:1:(root):/(root):/bin/nologin
bin:x:1:1:tom:/tom:/stom/nologin
daemon:x:2:2:jerry:/sbin:/stom/nologin
daemon:x:2:2:jerry:/sbin:/stom/nologin
adm:x:3:4:boss:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
Ethernet
#BOOTPROTO="dhcp"
HWADDR="00:0C:29:90:79:78"
ONBOOT="yes"
IPADDR=172.16.10.12
NETMASK=255.255.0.0
sed 引用變量 (腳本中也較多使用)
第一種當sed命令里面沒有默認的變量時可以把單引號改成雙引號
第二種當sed命令里面有默認的變量時,自己定義的變量需加單引號,且sed里面的語句必須用單引
#!/bin/sh
my_name=li
sed -i 's/'bbb' /'$my_name'/' /shell/myfile
sed的其他用法
操作一個文件,并寫入到另一個文件
[root@jie1 ~]# cat test #sed操作的文件中的內容
Ethernet
#BOOTPROTO="dhcp"
HWADDR="00:0C:29:90:79:78"
ONBOOT="yes"
IPADDR=172.16.10.12
NETMASK=255.255.0.0
[root@jie1 ~]# sed -i 's/IPADDR/ip/w ip.txt' test
#把sed操作的文件內容保存到另外一個文件中,w表示保存,ip.txt文件名
[root@jie1 ~]# cat ip.txt #查看新文件的內容
ip=172.16.10.12
讀取一個文件到正在用sed操作的文件中
[root@jie1 ~]# cat myfile #文件內容
hello world
i am li
how are you
li
[root@jie1 ~]# cat test #將用sed操作的文件的內容
Ethernet
#BOOTPROTO="dhcp"
HWADDR="00:0C:29:90:79:78"
ONBOOT="yes"
IPADDR=172.16.10.12
NETMASK=255.255.0.0
[root@jie1 ~]# sed -i '/Ethernet/r myfile' test
#在匹配Ethernet的行,讀進來另一個文件的內容,讀進來的文件的內容會插入到匹配Ethernet的行后
[root@jie1 ~]# cat test #再次查看用sed命令操作的行
Ethernet
hello world
i am li
how are you
li
#BOOTPROTO="dhcp"
HWADDR="00:0C:29:90:79:78"
ONBOOT="yes"
IPADDR=172.16.10.12
NETMASK=255.255.0.0
[root@jie1 ~]#
sed的經典例子
[root@jie1 ~]# cat myfile #文件內容
hello world
i am li
how are you
li
[root@jie1 ~]# cat test #將用sed操作的文件的內容
Ethernet
#BOOTPROTO="dhcp"
HWADDR="00:0C:29:90:79:78"
ONBOOT="yes"
IPADDR=172.16.10.12
NETMASK=255.255.0.0
[root@jie1 ~]# sed -i '/Ethernet/r myfile' test
#在匹配Ethernet的行,讀進來另一個文件的內容,讀進來的文件的內容會插入到匹配Ethernet的行后
[root@jie1 ~]# cat test #再次查看用sed命令操作的行
Ethernet
hello world
i am li
how are you
li
#BOOTPROTO="dhcp"
HWADDR="00:0C:29:90:79:78"
ONBOOT="yes"
IPADDR=172.16.10.12
NETMASK=255.255.0.0
[root@jie1 ~]#
AWK
基礎語法
awk [options] file ...
#逐行讀取并打印出來,默認以空格分割
awk '{print $0}' /etc/passwd
#讀取每行的第一個域的內容,并打印出來
awk -F: '{print $1}' /etc/passwd
#匹配的列,只要有匹配就打印,不分域
awk '/root/ {print $1}' /etc/passwd
#統計行數
awk '{count++}END{print count}' /etc/passwd
#count是自定義變量,這里沒有初始化count,雖然默認是0,但是妥當的做法還是初始化為0.
awk 'BEGIN{count=0}{count=count+1}END{print count}' /etc/passwd
#字符長度大于N的行
awk -F: 'length($1)>5' /etc/passwd
#統計某個文件夾下的文件占用的字節數
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}'
#按照M為單位顯示
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}'
內置函數
ARGC 命令行參數個數
ARGV 命令行參數排列
ENVIRON 支持隊列中系統環境變量的使用
FILENAME awk瀏覽的文件名
FNR 瀏覽文件的記錄數
FS 設置輸入域分隔符,等價于命令行 -F選項
NF 瀏覽記錄的域的個數
NR 已讀的記錄數
OFS 輸出域分隔符
ORS 輸出記錄分隔符
RS 控制記錄分隔符
#統計/etc/passwd:文件名,每行的行號,每行的列數,對應的完整行內容:
awk -F: '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd
#命令行參數個數
awk '{print ARGC}' file file2...
awk可以做很多事情,賦值,運算,變量等等...具體就不一一說明了,自行搜索.
sort
基本語法
sort [option] [files...]
選項
-b:忽略每行前面開始處的空格字符;
-c:檢查文件是否已經按照順序排序,排序過為真;
-d:排序時,處理英文字母、數字和空格字符,以字典順序排序。忽略其他所有字符;
-f:排序時,將小寫字母視為大寫字母;
-i:排序時,處理040~176之間的ASCII字符,忽略其他所有字符;
-m:將幾個排序好的文件進行合并;
-M:將前面3個字母按月份的縮寫進行排序;
-n:按照數值大小進行排序;
-o outfile.txt:將排序后的結果存入outfile.txt;
-r:以相反的順序進行排序;
-k:指定需要排序的列數(欄數);就是指定按第幾列進行排序
-t 分隔符:指定排序時所用到的欄位分隔符;
-u 去重 不過一般使用 uniq操作
例如,如下文本
one_two
one_two_three
one_two_four
one_two_five
### 以下劃線為分割 第一第二列進行排序,那么默認第三列會按順序進行排序,所以結果會被打亂
sort -t_ -k1,1 -k2,2 filename
[root/shell] ]$sort -t _ -k1,1 -k2,2 c
one_two
one_two_five
one_two_four
one_two_three
#所以,sort提供了--stable參數來進行補救。
sort -t_ -k1,1 -k2,2 filename
uniq
選項
-c, --count //在每行前加上表示相應行目出現次數的前綴編號
-d, --repeated //只輸出重復的行
-D, --all-repeated //只輸出重復的行,不過有幾行輸出幾行
-f, --skip-fields=N //-f 忽略的段數,-f 1 忽略第一段
-i, --ignore-case //不區分大小寫
-s, --skip-chars=N //根-f有點像,不過-s是忽略,后面多少個字符 -s 5就忽略后面5個字符
-u, --unique //去除重復的后,全部顯示出來,根MySQL的distinct功能上有點像
-z, --zero-terminated end lines with 0 byte, not newline
-w, --check-chars=N //對每行第N 個字符以后的內容不作對照
--help //顯示此幫助信息并退出
--version //顯示版本信息并退出
注意
二點!
1,對文本操作時,它一般會和sort命令進行組合使用,因為uniq 不會檢查重復的行,除非它們是相鄰的行。如果您想先對輸入排序,使用sort -u。
2,對文本操作時,若域中為先空字符(通常包括空格以及制表符),然后非空字符,域中字符前的空字符將被跳過
fmt文本塊操作
語法
fmt [option] [file-list]
選項
-s 截斷長行,但不合并
-t 除每個段落的第1行外都縮進
-u 改變格式化,使字之間出現一個空格,句子之間出現兩個空格
-w n 將輸出的行寬改為n個字符。不帶該選項時,fmt輸出的行寬度為75個字符
例如,我有一個文件demo,內容為:
A long time ago, there was a huge apple tree. A little boy loved to come and play around it every day. He climbed to the tree top, ate the apples, took a nap under the shadow… He loved the tree and the tree loved to play with him.
使用命令 fmt -s demo,輸出為:
A long time ago, there was a huge apple tree. A little boy loved
to come and play around it every day. He climbed to the tree top, ate
the apples, took a nap under the shadow… He loved the tree and the
tree loved to play with him.
該命令的含義是節段2長行.
使用fmt -t demo命令的意思是說排除首行的縮進,結果為:
A long time ago, there was a huge apple tree. A little boy loved
to come and play around it every day. He climbed to the tree top,
ate the apples, took a nap under the shadow… He loved the tree and
the tree loved to play with him.
使用fmt -u demo命令的意思是說格式化單詞和句子的間隔.輸出為:
A long time ago, there was a huge apple tree. A little boy loved to come
and play around it every day. He climbed to the tree top, ate the apples,
took a nap under the shadow… He loved the tree and the tree loved to
play with him.
顯然A little boy前面的多個空格變成了兩個.
使用命令fmt -w 40 demo意思是說指定行的寬度,這里的行寬為40個字符.所以輸出為:
A long time ago, there was a huge
apple tree. A little boy
loved to come and play around it
every day. He climbed to the tree top,
ate the apples, took a nap under the
shadow… He loved the tree and the
tree loved to play with him.
僅作切割的選項: -s , 在你想將長的行繞回,短的行保持不動時很好用,這么做也能使結果與原始版本間的差異達到最小,例如:
fmt -s -w 10 << EOF
one two three four five
six
seven
eight
輸出為:
one two
three
four five
six
seven
eight
fmt的小案例:
下面以拼音字典為例:
字典文件:/usr/dict/words或者/usr/share/dict/words。
sed -n -e 9991,10010p /usr/share/dict/words | fmt
sed -n -e 9991,10010p /usr/share/dict/words | fmt -w 30
wc
選項
-c:統計字節數
-l:統計行數
-m:統計字符數.這個標志不能與-c標志一起使用
-w:統計字數.一個字被定義為由空白,挑個或換行字符分隔的字符串.
-L:打印最常行的長度
-help:顯示幫助信息
--version:顯示版本信息
seq
-s 指定分隔符,默認是換行
-w 等位補全,就是寬度相等,不足的前面補 0
-f 格式化輸出,就是指定打印的格式
seq 1 10 #輸出1到10
seq -s "--" 1 3 #輸出1--2--3
seq -w 1 3 #等寬輸出
seq -f %03g 1 20 #格式化為五位,不足用0補齊
# %后面指定數字的位數 默認是%g,%3g那么數字位數不足部分是空格。