JB的Shell之旅-30分鐘帶你入門

前言

寫這篇文章的目的很簡單,由於愛並恨過;php

前段時間要改個安卓產品的腳本,驚奇發現理論是用shell,雖然實現的功能不復雜,但若是對沒了解過shell或懂皮毛的同窗,改起來是至關痛苦(如jb),調試半天各類找文檔,性價比過低了,巴不得用Python重構。。 html

image.png-38.4kB

雖然仍是能完成想要的結果,但花費的時間遠超一開始想的,目前shell給jb的感受就如去年正則帶來的噩夢通常,知道這玩意,會一丟丟,可是真正擼碼改內容的時候,查半天文檔,調試半天不如願;java

所以就有了該篇,對新同窗而言,目的是讓你快速瞭解shell,對於老同窗而言,目的是提供經常使用的shell命令快速喚醒記憶,而且貼出例子來快速實驗,大神請右上,謝謝~node

此處手動@敏爺,由於敏爺也諮詢了個shell的問題,建議都看看,鞏固下;linux

shell是什麼

shell是一個用C編寫的程序,是用戶使用Linux的橋樑;nginx

shell和shell腳本的概念

而日常說的shell就是指shell腳本,是一種爲shell編寫的腳本程序;c++

而shell還有另外一種概念,是指一種應用程序,這個應用程序提供了一個界面,用戶經過這個界面訪問操做系統內核的服務;
Ken Thompson的sh是第一種Unix Shell,Windows Explorer是一個典型的圖形界面Shell;git

用途

  • 自動化經常使用的命令
  • 執行系統管理和故障排除
  • 執行簡單的應用程序
  • 處理文本或文件

示例

本文中說的shell,大部分指shell腳本,那就來看示例吧,想知道shell腳本是什麼:正則表達式

#!/bin/sh
cd ~
mkdir jb_shell
cd jb_shell

for ((i=0; i<5; i++)); do
	touch jb_$i.txt
done
複製代碼

示例解釋:shell

  • 第1行:指定腳本解釋器,這裏是用/bin/sh作解釋器
  • 第2行:切換到當前用戶的home目錄
  • 第3行:建立一個目錄jb_shell
  • 第4行:切換到jb_shell目錄
  • 第6行:循環條件,一共循環5次,每次循環i+1
  • 第7行:建立一個jb_0-4.txt文件
  • 第8行:循環體結束

mkdirtouch都是系統自帶的程序,通常都在/bin或/usr/bin目錄下;

fordodonesh腳本語言的關鍵字;

環境

shell編程跟java、Python編程同樣,只要有一個能編寫代碼的文本編輯器和一個能解釋執行的腳本解釋器就能夠了;

OS

當前主流的操做系統都支持shell編程,本文檔所述的shell編程是指Linux下的shell,講的基本都是POSIX標準下的功能,因此,也適用於Unix及BSD(如Mac OS);

Linux

Linux默認安裝就帶了shell解釋器;

Mac OS

Mac OS不只帶了sh、bash這兩個最基礎的解釋器,還內置了ksh、csh、zsh等不經常使用的解釋器;

Windows上的模擬器

windows出廠時沒有內置shell解釋器,須要自行安裝,爲了同時能用grep, awk, curl等工具,最好裝一個cygwin或者mingw來模擬linux環境。

腳本解釋器

sh

即Bourne shell,POSIX(Portable Operating System Interface)標準的shell解釋器,它的二進制文件路徑一般是/bin/sh,由Bell Labs開發;

本文講的是sh,若是你使用其它語言用做shell編程,請自行參考相應語言的文檔。

bash

Bash是Bourne shell的替代品,屬GNU Project,二進制文件路徑一般是/bin/bash;
業界一般混用bash、sh、和shell,好比你會常常在招聘運維工程師的文案中見到:熟悉Linux Bash編程,精通Shell編程

在CentOS裏,/bin/sh是一個指向/bin/bash的符號連接:

[root@centosraw ~]# ls -l /bin/*sh
-rwxr-xr-x. 1 root root 903272 Feb 22 05:09 /bin/bash
-rwxr-xr-x. 1 root root 106216 Oct 17  2012 /bin/dash
lrwxrwxrwx. 1 root root      4 Mar 22 10:22 /bin/sh -> bash
複製代碼

但在Mac OS上不是,/bin/sh和/bin/bash是兩個不一樣的文件,儘管它們的大小隻相差100字節左右:

iMac:~ wuxiao$ ls -l /bin/*sh
-r-xr-xr-x  1 root  wheel  1371648  6 Nov 16:52 /bin/bash
-rwxr-xr-x  2 root  wheel   772992  6 Nov 16:52 /bin/csh
-r-xr-xr-x  1 root  wheel  2180736  6 Nov 16:52 /bin/ksh
-r-xr-xr-x  1 root  wheel  1371712  6 Nov 16:52 /bin/sh
-rwxr-xr-x  2 root  wheel   772992  6 Nov 16:52 /bin/tcsh
-rwxr-xr-x  1 root  wheel  1103984  6 Nov 16:52 /bin/zsh
複製代碼

高級編程語言

理論上講,只要一門語言提供瞭解釋器(而不只是編譯器),這門語言就能夠勝任腳本編程,常見的解釋型語言都是能夠用做腳本編程的,如:Perl、Tcl、Python、PHP、Ruby;

Perl是最老牌的腳本編程語言了,Python這些年也成了一些linux發行版的預置解釋器;

編譯型語言,只要有解釋器,也能夠用做腳本編程,如C shell是內置的(/bin/csh),Java有第三方解釋器Jshell,Ada有收費的解釋器AdaScript;

以下是一個PHP Shell Script示例(假設文件名叫test.php):

#!/usr/bin/php
<?php
for ($i=0; $i < 10; $i++)
        echo $i . "\n";
複製代碼

執行:

/usr/bin/php test.php
複製代碼

或者:

chmod +x test.php
./test.php
複製代碼

第一個shell腳本

編寫

打開文本編輯器,新建一個文件,擴展名爲sh(sh表明shell),擴展名並不影響腳本執行;

image.png-1.8kB

輸入一些代碼,第一行通常是這樣:

#!/bin/bash
複製代碼

#!是一個約定的標記,它告訴系統這個腳本須要什麼解釋器來執行;

運行

運行Shell腳本有兩種方法:

做爲可執行程序

chmod +x test.sh
./test.sh
複製代碼

注意,必定要寫成./test.sh,而不是test.sh;
運行其它二進制的程序也同樣,直接寫test.sh,linux系統會去PATH裏尋找有沒有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH裏,你的當前目錄一般不在PATH裏;
因此寫成test.sh是會找不到命令的,要用./test.sh告訴系統說,就在當前目錄找;

image.png-6.8kB
經過這種方式運行bash腳本,第一行必定要寫對,好讓系統查找到正確的解釋器;

這裏會有疑問,#!/bin/bash能夠不寫嗎?
答案是能夠的,若是這個系統是使用/bin/bash做爲解釋器的話,就能夠省去,或者使用解釋器參數運行就不須要;

做爲解釋器參數 這種運行方式是,直接運行解釋器,其參數就是shell腳本的文件名,如:

/bin/sh test.sh
bash test.sh
複製代碼

image.png-6.9kB

這種方式運行的腳本,不須要在第一行指定解釋器信息,寫了也沒用。

變量

定義變量

定義變量時,變量名不須要加美圓符號($),如:

your_name="jb"  # 這種狀況,雙引號可加可不加
your_name="jb test" #若是有空格要用雙引號
複製代碼

注意,變量名和等號之間不能有空格,這裏踩坑屢次,切忌切忌,用其餘語言的時候習慣空格了;

使用變量

使用一個定義過的變量,只要在變量名前面加美圓符號便可,如:

your_name="jb"
echo $your_name
複製代碼

echo ${your_name} 變量名外面的花括號是可選的,加不加都行,加花括號是爲了幫助解釋器識別變量的邊界,好比下面這種狀況:

for girl in 蒼老師 前田 波多 大欖; do
	echo "I like ${girl}Style"
done
複製代碼

若是不給girl變量加花括號,寫成echo "I like ${girl}Style",解釋器就會把$girlStyle當成一個變量(其值爲空),代碼執行結果就不是想要的效果了;

重定義變量

已定義的變量,能夠被從新定義,如:

your_name="jb"
echo $your_name

your_name="jbtest"
echo $your_name
複製代碼

這樣寫是合法的,但注意,第二次賦值的時候不能寫$your_name="jbtest"使用變量的時候才加美圓符

註釋

#開頭的行就是註釋,會被解釋器忽略;

多行註釋

sh裏沒有多行註釋,只能每一行加一個#號。就像這樣:

#--------------------------------------------
#jb test 
複製代碼

可是,若是有幾十行的代碼要臨時註釋,每個都價格#號太麻煩了,那有沒有騷操做了?

有的,就是利用:<<連個,那先來介紹這兩個玩意:

:冒號是一個空命令,能夠認爲與shell的內建命令true相同,它的返回值是0;

在while循環中 while : 與 while true 的做用是等效的; 在 if/then 中可做爲佔位符;

if conditions
then:  #什麼都不作
else
take action 
fi
複製代碼

<< tag是將開始標記 tag 和結束標記 tag 之間的內容做爲輸入,tag不是關鍵字,能夠隨意替換,只是要先後統一便可;

那另一種多行註釋的方式以下:

:<<jbtest
echo "I am jb"
echo "I am jb"
echo "I am jb"
jbtest
複製代碼

:冒號空命令,啥都不作,<<是將內容重定向輸入,兩個jbtest(可任意替換,不是關鍵字)之間的內容經過<<追加給冒號:,可是:冒號對它們啥都不作,就至關於沒作任何處理和輸出,就至關於註釋了;

這操做,的確牛,可是會有兼容性問題,如註釋的內容裏面帶有冒號的話,會報錯;

image.png-84.6kB

字符串

字符串是shell編程中最經常使用最有用的數據類型,字符串能夠用單引號,也能夠用雙引號,也能夠不用引號

單引號

str='this is jb'
複製代碼

單引號字符串的限制:

  • 單引號裏的任何字符都會原樣輸出,單引號字符串中的變量是無效的
  • 單引號字串中不能出現單引號(對單引號使用轉義符後也不行)

雙引號

your_name='jb'
str="Hello, I know your are \"$your_name\"! \n"
複製代碼
  • 雙引號裏能夠有變量
  • 雙引號裏能夠出現轉義字符

字符串操做

拼接字符串

your_name="jb"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"

echo $greeting $greeting_1

輸出:hello, jb ! hello, jb !
複製代碼

獲取字符串長度:

取字符串長度;

string="jb"
echo ${#string} 
expr length $string

兩個都是輸出4,但通常用第一種較多;
複製代碼

提取字符串

取特定長度的字符串內容;

string="jb is a jb"
echo ${string:0:4} # 輸出jb i,從第0個位置開始提取4個字符串;
echo ${string:4} # 輸出s a jb,從第4個位置開始提取字符串
echo ${string:(-6):3}
# 輸出s a,負數表示從右開始計數,從右側數起第6個位置提取3個字符串;
複製代碼

查找字符串位置

找出具體字符串位置;

str="abc"
expr index $str "ab"  # 輸出:1
expr index $str "b"  # 輸出:2
expr index $str "x"  # 輸出:0,不存在則0
複製代碼

截取子串

str="abbc,def,ghi,abcjkl"

命令 意義&輸出
echo${str#a*c} 輸出,def,ghi,abcjkl 一個井號(#) 表示從左邊截取掉最短的匹配 (這裏把abbc字串去掉)
echo ${str##a*c} 輸出jkl 兩個井號(##) 表示從左邊截取掉最長的匹配 (這裏把abbc,def,ghi,abc字串去掉)
echo ${str#"a*c"} 輸出abbc,def,ghi,abcjkl 由於str中沒有"a*c"子串
echo ${str##"a*c"} 輸出abbc,def,ghi,abcjkl 同理
echo ${str#d*f} 輸出abbc,def,ghi,abcjkl
echo ${str#*d*f} 輸出,ghi,abcjkl
echo ${str%a*l} 輸出abbc,def,ghi, 一個百分號(%)表示從右邊截取最短的匹配
echo ${str%%b*l} 輸出a 兩個百分號表示(%%)表示從右邊截取最長的匹配
echo ${str%a*c} 輸出abbc,def,ghi,abcjkl

1個井號#帶一個數字,因此截取最短,2個井號則截取最長; 1個百分號%只是從右側開始算,同上;

也許上面有同窗會有疑問echo ${str#d*f},而什麼會顯示所有解決,由於這種方式是從頭開始的,是字符串匹配的,真不智能了,所以列出其餘能夠截取字符串的方式:

格式 說明
${string: start :length} 從 string 字符串的左邊第 start 個字符開始,向右截取 length 個字符;
${string: start} 從 string 字符串的左邊第 start 個字符開始截取,直到最後;
${string: 0-start :length} 從 string 字符串的右邊第 start 個字符開始,向右截取 length 個字符;
${string: 0-start} 從 string 字符串的右邊第 start 個字符開始截取,直到最後;
${string#*chars} 從 string 字符串第一次出現 *chars 的位置開始,截取 *chars 右邊的全部字符;
${string##*chars} 從 string 字符串最後一次出現 *chars 的位置開始,截取 *chars 右邊的全部字符;
${string%*chars} 從 string 字符串第一次出現 *chars 的位置開始,截取 *chars 左邊的全部字符;
${string%%*chars} 從 string 字符串最後一次出現 *chars 的位置開始,截取 *chars 左邊的全部字符;

字符串替換

str="apple, tree, apple tree"
echo ${str/apple/APPLE}
#替換第一次出現的apple,輸出:APPLE, tree, apple tree

echo ${str//apple/APPLE}  
# 替換全部apple,輸出:APPLE, tree, APPLE tree

echo ${str/#apple/APPLE}
# 若是字符串str以apple開頭,則用APPLE替換它,輸出:APPLE, tree, apple tree

echo ${str/%apple/APPLE}  
# 若是字符串str以apple結尾,則用APPLE替換它,輸出:apple, tree, apple tree
複製代碼

這裏面也有明顯的問題,就是若是想替換第2、第三個怎麼辦? 抱歉,是不行的,只能用sed,下面會說起到;

數組

數組中能夠存放多個值。 Shell 只支持一維數組(不支持多維數組),初始化時不須要定義數組大小; 與大部分編程語言相似,數組元素的下標由0開始;

Shell 數組用括號來表示,元素用空格符號分割開,語法格式以下:

array_name=(value1 ... valuen)
arr_num=(1 2 3 4 5 )
arr_string=("abc" "edf" "sss")
複製代碼

也可使用下標來定義數組:

array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
複製代碼

獲取數組長度

下面兩種方式都可  

${#arr_number[*]}

${#arr_number[@]}
複製代碼

例子:

arr_number=(1 2 3 4 5)
echo "數組元素個數爲: ${#arr_number[*]}"

echo "數組元素個數爲: ${#arr_number[@]}"

輸出:5
複製代碼

讀取某個下標的值

arr_number=(1 2 3 4 5)
echo "${#arr_number[2]}"

${數組名[下標]}
輸出:1
複製代碼

對某個下標進行賦值

這裏分兩種狀況: 指定的下標是存在的 直接替換新的指定值;

arr_number[2]=100,數組被修改成(1 2 100 4 5)
複製代碼

指定的下標是不存在的 那新的賦值會添加後數組的尾部;

arr_number[100]=100,數組被修改成(1 2 100 4 5 100)
複製代碼

刪除

清除某個元素:

unset arr_number[1]
# 這裏清除下標爲1的數組;
複製代碼

清空整個數組:

unset arr_number
複製代碼

分片訪問

分片訪問形式爲:${數組名[@或*]:開始下標:結束下標}, 注意,不包括結束下標元素的值;

${arr_number[@]:1:4}
# 這裏分片訪問從下標爲1開始,元素個數爲4,輸出的是2 3 4 5 
複製代碼

數值替換

形式爲:${數組名[@或*]/數值/新值}

${arr_number[@]/2/98}
#就是把2替換成98,輸出:1 98 3 4 5
複製代碼

數組遍歷

for v in ${arr_number[@]}; do
    echo $v;
done
複製代碼

傳參

命令行參數

通常使用腳本,都須要向腳本傳遞參數,那shell獲取參數的格式是$n, n表明的是數據,1爲執行腳本的第一個參數,2爲執行腳本的第二位參數;

echo "Shell 傳遞參數實例!";
echo "執行的文件名:$0";
echo "第一個參數爲:$1";
echo "第二個參數爲:$2";
echo "第三個參數爲:$3";  
複製代碼

執行腳本:

$ bash test.sh one two three
Shell 傳遞參數實例!
執行的文件名:test.sh
第一個參數爲:one
第二個參數爲:two
第三個參數爲:three
複製代碼

特殊參數

還有幾個特殊字符,這邊不展開說明,自行了解吧;

參數處理 說明
$# 傳遞到腳本的參數個數
$* 以一個單字符串顯示全部向腳本傳遞的參數。如"$*"用「"」括起來的狀況、以"$1 2 …n"的形式輸出全部參數。
$$ 腳本運行的當前進程ID號
$! 後臺運行的最後一個進程的ID號
$@ 與$*相同,可是使用時加引號,並在引號中返回每一個參數。

如"$@"用「"」括起來的狀況、以"$1" "2" … "n" 的形式輸出全部參數。 $- |顯示Shell使用的當前選項,與set命令功能相同。 $? |顯示最後命令的退出狀態。0表示沒有錯誤,其餘任何值代表有錯誤。

處理選項

若是腳本傳參有N個,可是又不想一個一個$1$2這樣判斷,那怎麼辦?

while [ -n "$1" ]
do 
	case "$1" in
	-jb) echo "jb";;
	-jb1) echo "jb1";;
	-jb2) echo "jb2";;
	-jb3) echo "jb3";;
	*) echo "over"	;;
	esac 
	shift
done
複製代碼

運行的結果:

$ bash test.sh -jb -jb2 -jb3 -jb4
jb
jb2
jb3
over
複製代碼

管道

在 Bash 中,管道符使用"丨"表明; 管道符也是用來鏈接多條命令的,如"命令1丨命令2";

  • 命令 1 的正確輸出做爲命令 2 的操做對象;
  • 命令 1 必須有正確輸出,而命令 2 必須能夠處理命令 1 的輸出結果;
  • 命令 2 只能處理命令 1 的正確輸出,而不能處理錯誤輸出;

好比:

adb shell ps | grep com
複製代碼

就會輸出一堆喊出com的內容,也就是利用了管道符號;

image.png-40.3kB

條件判斷

條件判斷的做用 判斷某需求是否知足;

返回類型

  • true,返回0
  • false,返回1

格式

  • test 判斷式
  • [ 判斷式 ] (經常使用)

按照文件類型進行判斷

測試選項 做用
-b 文件 判斷該文件是否存在,而且是塊設備文件
-c 文件 判斷該文件是否存在,而且是字符設備文件
-d 文件 判斷該文件是否存在,而且是目錄
-e 文件 判斷該文件是否存在
-f 文件 判斷該文件是否存在,而且是普通文件
-L 文件 判斷該文件是否存在,而且是符號連接文件
-p 文件 判斷該文件是否存在,而且是管道文件
-s 文件 判斷該文件是否存在,而且非空
-S 文件 判斷該文件是否存在,而且是套接字文件

利用 &&|| 判斷文件是不是目錄

$ [ -d test ] && echo 'yes' || echo 'no'
yes

$ [ -d test1 ] && echo 'yes' || echo 'no'
no
複製代碼

按照文件權限進行判斷

測試選項 做用
-r 文件 判斷該文件是否存在,而且當前用戶擁有讀權限
-w 文件 判斷該文件是否存在,而且當前用戶擁有寫權限
-x 文件 判斷該文件是否存在,而且當前用戶擁有執行權限
-u 文件 判斷該文件是否存在,而且擁有SUID權限
-g 文件 判斷該文件是否存在,而且擁有SGID權限
-k 文件 判斷該文件是否存在,而且擁有SBit權限

判斷文件權限

$  [ -r test ] && echo yes || echo no
yes

$  [ -w test ] && echo yes || echo no
yes

$  [ -x test ] && echo yes || echo no
yes
複製代碼

兩個文件之間進行比較

測試選項 做用
文件1 -nt 文件2 判斷文件1的修改時間是否比文件2的新
文件1 -ot 文件2 判斷文件1的修改時間是否比文件2的舊
文件1 -ef 文件2 判斷文件1和文件2的Inode號是否一致,能夠理解爲兩個文件是否爲同一個文件。這適用於判斷硬連接很好的方法

比較兩個文件的最後修改時間

  • 相隔1秒,建立file1和file2兩個文件
$ touch jb1; sleep 5; touch jb22

$ ll jb*
-rw-r--r-- 1 jb 197121 0 12月 11 15:17 jb
-rw-r--r-- 1 jb 197121 0 12月 11 15:17 jb1
-rw-r--r-- 1 jb 197121 0 12月 11 15:17 jb22
複製代碼
  • 比較兩個文件的最後修改時間
jb@LAPTOP-2R0431R1 MINGW64 /c
$ [ jb1 -ot jb22 ] && echo yes || echo no
yes

jb@LAPTOP-2R0431R1 MINGW64 /c
$ [ jb1 -nt jb22 ] && echo yes || echo no
no

複製代碼

兩個整數之間比較

測試選項 做用
整數1 -eq 整數2 判斷整數1是否等於整數2
整數1 -ne 整數2 判斷整數1是否不等於整數2
整數1 -gt 整數2 判斷整數1是否大於整數2
整數1 -lt 整數2 判斷整數1是否小於整數2
整數1 -ge 整數2 判斷整數1是否大於等於整數2
整數1 -le 整數2 判斷整數1是否小於等於整數2

字符串的判斷

測試選項 做用
-z 字符串 判斷字符串是否爲空
-n 字符串 判斷字符串是否爲非空
字符串1 == 字符串2 判斷字符串1和字符串2是否相等
字符串1 != 字符串2 判斷字符串1和字符串2是否不相等
  • 字符串1 == 字符串2:判斷字符串1和字符串2是否相等
$ a=111
$ b=222
$ [ "$a" == "$b" ] && echo yes || echo no
no
複製代碼

多重條件判斷

測試選項 做用
判斷1 -a 判斷2 邏輯與,判斷1 和 判斷2都爲真,最終結果才爲真
判斷1 -o 判斷2 邏輯或,判斷1 和 判斷2有一個爲真,最終結果就爲真
! 判斷 邏輯非,使原始的判斷式取反

流程控制

if else

if

if condition
then
	command1 
	command2
	...
	commandN 
fi
複製代碼

寫成一行(適用於終端命令提示符):

if `ps -ef | grep ssh`;  then echo hello; fi
複製代碼

末尾的fi就是if倒過來拼寫,對成對出現的;

if else

if condition
then
	command1 
	command2
	...
	commandN
else
	command
fi
複製代碼

if else-if else

if condition1
then
	command1
elif condition2
	command2
else
	commandN
fi
複製代碼

for while

for

在上面的示例裏看到過:

for var in item1 item2 ... itemN
do
	command1
	command2
	...
	commandN
done
複製代碼

寫成一行:

for var in item1 item2 ... itemN; do command1; command2… done;
複製代碼

while

while循環用於不斷執行一系列命令,也用於從輸入文件中讀取數據;

while condition
do
	command
done
複製代碼

無限循環

while :
do
	command
done
複製代碼

或者

while true
do
	command
done
複製代碼

或者

for (( ; ; ))
複製代碼

until

until 循環執行一系列命令直至條件爲 true 時中止; until 循環與 while 循環在處理方式上恰好相反;

通常 while 循環優於 until 循環,但在某些時候—也只是極少數狀況下,until 循環更加有用。

until condition
do
	command
done
複製代碼

case

Shell case語句爲多選擇語句。能夠用case語句匹配一個值與一個模式,若是匹配成功,執行相匹配的命令。

case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac
複製代碼

須要一個esac(就是case反過來)做爲結束標記,每一個case分支用右圓括號用兩個分號表示break;

跳出循環

break

break命令容許跳出全部循環(終止執行後面的全部循環)。

continue

ontinue命令與break命令相似,只有一點差異,它不會跳出全部循環,僅僅跳出當前循環。

函數

shell能夠自定義函數,而後在shell腳本中隨意調用;

定義

shell函數定義的格式:

function 函數名 () {  
        to do..
        return -n  
}  
複製代碼
  • 能夠function 函數名()定義,也能夠直接函數名()定義,不帶任何參數;
  • return是可選,不加則將以最後一條命令運行結果做爲返回值;

調用

function jb1 () {  #前面的function是聲明一個函數 名字叫 jb1 ()
    echo "jb1"   # 執行操做,輸出jb1
}  
  
function jb2 () {  
    echo "jb2"  
}  

jb3() {
	echo "jb3"
}


jb1    # 調用jb1函數
jb2
jb3
複製代碼

帶參數函數實例

function jb () {  
        echo 個人名字叫: $1  
}  
jb $1 
複製代碼

運行:

$ bash test.sh hahhahha
個人名字叫: hahhahha
複製代碼

使用函數實現菜單腳本

function CDAN(){  
cat << yankerp    
+------------------------------------------------+  
|                                                |  
|        _o0o_          1. 安裝Nginx             |  
|        08880          2. 安裝Apache            |  
|       88"."88         3. 安裝MySQL             |  
|       (|-_-|)         4. 安裝PHP               |  
|        0\=/0          5. 部署LNMP環境          |  
|      __/   \__        6. 安裝zabbix監控        |  
|     ‘\   ///‘       7. 退出此管理程序         |  
|    / Linux一鍵 \      8. 關閉計算機            |  
|  ||    Server   ||    ======================   |    
|  \        ////         一鍵安裝服務           |  
|   |||  i i i    |||               by Yankerp   |  
|   ___        ___      ======================   |  
|___‘.  /--.--\ .‘___                            |  
+------------------------------------------------+  
yankerp  
}  
CDAN  
  
LOG_DIR=/usr/local/src  
read -p "請您輸入1-8任意數值:" NUM  
  
if [ ${#NUM} -ne 1 ]  
  then  
        echo "請您輸入1|2|3|4|5|6|7|8"  
        exit 1  
fi  
  
expr $NUM + 1 &>/dev/null  
if [ "$?" -ne 0 ]  
   then  
        echo "請您輸入數值!"  
        exit 1  
fi  
  
if [ "$NUM" -gt 8 ];then  
        echo "請您輸入比8小的數值"  
        exit 1  
elif [ "$NUM" -eq 0 ];then  
        echo "請您輸入比0大的數值"  
        exit 1  
fi  
###################### 
function Nginx_DIR() {  
        yum install -y gcc gcc-c++ pcre-devel zlib-devel openssl-devel &>/dev/null  
        if [ $? -eq 0 ]  
           then  
                cd $LOG_DIR  && wget http://nginx.org/download/nginx-1.12.2.tar.gz &>/dev/null && useradd -M -s /sbin/nologin nginx && \  
        tar zxf nginx-1.12.2.tar.gz && cd nginx-1.12.2/ && \  
                ./configure --prefix=/usr/local/nginx --with-http_dav_module --with-http_stub_status_module --with-http_addition_module --with-http_sub_module  --with-http_flv_module --with-http_mp4_module --with-pcre --with-http_ssl_module --with-http_gzip_static_module --user=nginx &>/dev/null && make &>/dev/null && make install &>/dev/null   
        fi  
  
        if [ -e /usr/local/nginx/sbin/nginx ];then  
                ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/ && nginx && echo "Nginx安裝並啓動成功!!!"  
        fi  
}  
  
if [ $NUM -eq 1 ]  
  then  
     echo "開始安裝Nginx請稍等..." && Nginx_DIR  
fi
複製代碼

輸出:

image.png-97.5kB

select菜單

上面說了流程,這裏就介紹一個比較特殊的流程控制:select

select的格式以下:

select name   [in   list ] 
do 
    循環體命令
done
複製代碼

從格式上看,跟for的格式相似; select 循環主要用於建立菜單,按數字順序排列的菜單項將顯示在標準錯誤上,並顯示PS3 提示符,等待用戶輸入;

用戶輸入菜單列表中的某個選項,執行響應的命令,而輸入的內容會被保存在內置變量REPLY 裏;

select 是個無限循環,所以要記住用break 命令退出循環,或用exit 命令終止腳本。也能夠按ctrl+c 退出循環。

例子:

text="Please select a number: "
select name in jb1 jb2 jb3 jb4
do
    case $name in
    jb1)
        echo "Hello, jb1."
        ;;
    jb2)
        echo "Hello,jb2."
        ;;
    jb3)
        echo "Hello, jb3."
        ;;
    jb4)
        echo "Hello, jb4."
        ;;
    *)
        echo "Sorry, there is no such person."
        ;;
    esac
done
複製代碼

結果:

image.png-9kB

stdin&stdout&stderr

在Linux系統中,一切設備都看做文件。 而每打開一個文件,就有一個表明該打開文件的文件描述符。 程序啓動時默認打開三個I/O設備文件:

  • 標準輸入文件stdin,文件描述符0;
  • 標準輸出文件stdout,文件描述符1;
  • 標準錯誤輸出文件stderr,文件描述符2;
文件描述符 縮寫 描述
0 STDIN 標準輸入
1 STDOUT 標準輸出
2 STDERR 標準錯誤

默認狀況下,command > file 將 stdout 重定向到 file,command < file 將stdin 重定向到 file。

若是但願 stderr 重定向到 file,能夠這樣寫:

$ command 2 > file
複製代碼

若是但願 stderr 追加到 file 文件末尾,能夠這樣寫:

$ command 2 >> file
複製代碼

2 表示標準錯誤文件(stderr)。

若是但願將 stdout 和 stderr 合併後重定向到 file,能夠這樣寫:

$ command > file 2>&1
複製代碼

或者

$ command >> file 2>&1
複製代碼

若是但願對 stdin 和 stdout 都重定向,能夠這樣寫:

$ command < file1 >file2
複製代碼

command 命令將 stdin 重定向到 file1,將 stdout 重定向到 file2。

經常使用的命令

sh腳本結合系統命令便有了強大的威力,在字符處理領域,有grep、awk、sed三劍客,grep負責找出特定的行,awk能將行拆分紅多個字段,sed則能夠實現更新插入刪除等寫操做。

ps

ps命令用來列出系統中當前運行的那些進程,但ps 提供的hi進程的一次性的查看,它所提供的查看結果並非動態連續的;

若是想對進程時間監控,應該用 top 工具。

對系統中進程進行監測控制,查看狀態,內存,CPU的使用狀況,使用命令:/bin/ps

  • ps :是顯示瞬間進程的狀態,並不動態連續;
  • top:若是想對進程運行時間監控,應該用 top 命令;

做用

ps 命令就是最基本同時也是很是強大的進程查看命令; 使用該命令能夠肯定有哪些進程正在運行和運行的狀態、進程是否結束、進程有沒有僵死、哪些進程佔用了過多的資源等等。

命令行格式

使用方式:ps [options] [--help] 說明:顯示瞬間行程 (process) 的動態 參數:見下方

image.png-39.8kB

參數

命令 含義
ps a 顯示現行終端機下的全部程序,包括其餘用戶的程序
ps -A 顯示全部進程
ps c 列出程序時,顯示每一個程序真正的指令名稱
ps -e 此參數的效果和指定"A"參數相同
ps e 列出程序時,顯示每一個程序所使用的環境變量
ps f 用ASCII字符顯示樹狀結構,表達程序間的相互關係
ps -H 顯示樹狀結構,表示程序間的相互關係
ps -N 顯示全部的程序,除了執行ps指令終端機下的程序以外
ps s 採用程序信號的格式顯示程序情況
ps S 列出程序時,包括已中斷的子程序資料
ps -t<編號> 指定編號,並列出屬於該程序的情況
ps u  以用戶爲主的格式來顯示程序情況
ps x  顯示全部程序,不以程序來區分

最經常使用的方法是ps -aux,而後再利用一個管道符號導向到grep去查找特定的進程,而後再對特定的進程進行操做。

ps -aux | grep com.jbtest
#這樣就會把com.jbtest相關的進程信息所有列出;
複製代碼

經常使用

基本PS使用

$ps
複製代碼

image.png-20.2kB

名稱 含義
PID 運行着的命令(CMD)的進程編號
TTY 命令所運行的位置(終端)
TIME 運行着的該命令所佔用的CPU處理時間
CMD 該進程所運行的命令

列出目前全部的正在內存當中的程序

$ ps -aux
複製代碼

image.png-171.8kB

名稱 含義
USER 用戶名
UID 用戶ID(User ID)
PID 進程ID(Process ID)
PPID 父進程的進程ID(Parent Process id)
SID 會話ID(Session id)
%CPU 進程的cpu佔用率
%MEM 進程的內存佔用率
VSZ 進程所使用的虛存的大小(Virtual Size)
RSS 進程使用的駐留集大小或者是實際內存的大小,Kbytes字節。
TTY 與進程關聯的終端(tty)
STAT 進程的狀態:進程狀態使用字符表示的(STAT的狀態碼)
START 進程啓動時間和日期
TIME 進程使用的總cpu時間
COMMAND 正在執行的命令行命令

關於STAT,能夠經過截圖看到有不少字符,具體的含義以下:

名稱 含義
R 運行 Runnable (on run queue) 正在運行或在運行隊列中等待。
S 睡眠 Sleeping 休眠中, 受阻, 在等待某個條件的造成或接受到信號。
I 空閒 Idle
Z 僵死 Zombie(a defunct process) 進程已終止, 但進程描述符存在, 直到父進程調用wait4()系統調用後釋放。
D 不可中斷 Uninterruptible sleep (ususally IO) 收到信號不喚醒和不可運行, 進程必須等待直到有中斷髮生。
T 終止 Terminate 進程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信號後中止運行運行。
P 等待交換頁
W 無駐留頁 has no resident pages 沒有足夠的記憶體分頁可分配。
X 死掉的進程
< 高優先級進程 高優先序的進程
N 低優先 級進程 低優先序的進程
L 內存鎖頁 Lock 有記憶體分頁分配並縮在記憶體內
s 進程的領導者(在它之下有子進程);
l 多進程的(使用 CLONE_THREAD, 相似 NPTL pthreads)
+ 位於後臺的進程組

能夠用 | 管道和 more 鏈接起來分頁查看

ps -aux |more
複製代碼

能夠用 | 管道和 grep 過濾

ps -aux |grep ps
複製代碼

image.png-48.5kB

把全部進程顯示出來,並輸出到jb.txt文件

ps -aux > jb.txt
複製代碼

輸出指定的字段

ps -o pid,ppid,pgrp,session,tpgid,comm
複製代碼

image.png-40kB

根據 CPU 使用來升序排序

ps -aux --sort -pcpu | less
複製代碼

根據 內存使用 來升序排序

ps -aux --sort -pmem | less
複製代碼

樹形顯示進程

pstree
複製代碼

image.png-119.8kB

經過進程名和PID過濾 使用 -C 參數,後面跟你要找的進程的名字。好比想顯示一個名爲getty的進程的信息,就可使用下面的命令:

ps -C getty
複製代碼

顯示全部進程信息,連同命令行

ps -ef
複製代碼

grep

grep命令用於查找文件裏符合條件的字符串;

通常是結合管道|來使用,固然也支持單獨帶參使用;

grep 更適合單純的查找或匹配文本

語法

grep [-abcEFGhHilLnqrsvVwxy][-A<顯示列數>][-B<顯示列數>][-C<顯示列數>][-d<進行動做>][-e<範本樣式>][-f<範本文件>][--help][範本樣式][文件或目錄...]
複製代碼

參數說明

參數 含義
-a 或 --text 不要忽略二進制的數據。
-A<顯示行數> 或 --after-context=<顯示行數> 除了顯示符合範本樣式的那一列以外,並顯示該行以後的內容。
-b 或 --byte-offset 在顯示符合樣式的那一行以前,標示出該行第一個字符的編號。
-B<顯示行數> 或 --before-context=<顯示行數> 除了顯示符合樣式的那一行以外,並顯示該行以前的內容。
-c 或 --count 計算符合樣式的列數。
-C<顯示行數> 或 --context=<顯示行數>或-<顯示行數> 除了顯示符合樣式的那一行以外,並顯示該行以前後的內容。
-d <動做> 或 --directories=<動做> 當指定要查找的是目錄而非文件時,必須使用這項參數,不然grep指令將回報信息並中止動做。
-e<範本樣式> 或 --regexp=<範本樣式> 指定字符串作爲查找文件內容的樣式。
-E 或 --extended-regexp 將樣式爲延伸的普通表示法來使用。
-f<規則文件> 或 --file=<規則文件> 指定規則文件,其內容含有一個或多個規則樣式,讓grep查找符合規則條件的文件內容,格式爲每行一個規則樣式。
-F 或 --fixed-regexp 將樣式視爲固定字符串的列表。
-G 或 --basic-regexp 將樣式視爲普通的表示法來使用。
-h 或 --no-filename 在顯示符合樣式的那一行以前,不標示該行所屬的文件名稱。
-H 或 --with-filename 在顯示符合樣式的那一行以前,表示該行所屬的文件名稱。
-i 或 --ignore-case 忽略字符大小寫的差異。
-l 或 --file-with-matches 列出文件內容符合指定的樣式的文件名稱。
-L 或 --files-without-match 列出文件內容不符合指定的樣式的文件名稱。
-n 或 --line-number 在顯示符合樣式的那一行以前,標示出該行的列數編號。
-q 或 --quiet或--silent 不顯示任何信息。
-r 或 --recursive 此參數的效果和指定"-d recurse"參數相同。
-s 或 --no-messages 不顯示錯誤信息。
-v 或 --revert-match 顯示不包含匹配文本的全部行。
-V 或 --version 顯示版本信息。
-w 或 --word-regexp 只顯示全字符合的列。
-x --line-regexp 只顯示全列符合的列。
-y 此參數的效果和指定"-i"參數相同。

經常使用

上面說的不少參數,但通常來講,經常使用的很是少,那就來一塊兒看看經常使用的命令吧;

能夠用 | 管道和 grep 過濾 查找指定進程

ps -aux |grep ps
複製代碼

image.png-48.5kB

在當前目錄中,查找後綴有 jb 字樣的文件中包含 jbtest 字符串的文件

grep jbtest *jb 
複製代碼

上面這種用法,當目錄下有文件夾,就會報錯,grep: sound: Is a directory,由於grep默認只在當前目錄,若是須要遞歸查找,則須要使用-r;

grep -r jbtest /etc/jb 
#這樣就會輸出jb目錄下的全部文件包含jbtest的文件及內容
複製代碼

從文件中讀取關鍵詞進行搜索

cat jb.txt | grep -f jb2.txt
#意思是根據grep的結果來進行對jb.txt的過濾

[root@localhost test]# cat jb.txt 
hnlinux
peida.cnblogs.com
ubuntu
ubuntu linux
redhat
Redhat
linuxmint
[root@localhost test]# cat jb2.txt 
linux
Redhat
[root@localhost test]# cat jb.txt | grep -f jb2.txt
hnlinux
ubuntu linux
Redhat
linuxmint
複製代碼

顯示包含ed或者at字符的內容行

cat jb.txt |grep -E "ed|at"
複製代碼

正則匹配全部以字母 「b」 開頭、字母 「t」 結尾的三個字符的單詞

grep '\<b.t\>' jb.txt
複製代碼

awk

awk是什麼 awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤其強大;

簡單來講awk就是把文件逐行的讀入,以空格爲默認分隔符將每行切片,切開的部分再進行各類分析處理;

awk更適合格式化文本,對文本進行較複雜格式處理

語法

awk [選項參數] 'script' var=value file(s)
or
awk [選項參數] -f scriptfile var=value file(s)
複製代碼
字符 含義
F fs 表示指定輸入文件分隔符,fs是一個字符串或者是一個正則表達式,如-F;
-v var=value 表示賦值一個用戶定義變量;
-f scriptfile 從腳本文件中讀取awk命令;
-W compact 在兼容模式下運行awk;
-W copyleft 打印簡短的版權信息;
-W help 打印所有awk選項和每一個選項的簡短說明;
-W lint 打印不能向傳統unix平臺移植的結構的警告;
-W lint-old 打印關於不能向傳統unix平臺移植的結構的警告;
-W posix 打開兼容模式,但有如下限制,不識別:/x、函數關鍵字、func、換碼序列以及當fs是一個空格時,將新行做爲一個域分隔符;操做符**和**=不能代替^和^=;fflush無效。
-W re-interval 容許間隔正則表達式的使用,如括號表達式[[:alpha:]];
-W source program-text 使用program-text做爲源代碼;
-W version 打印bug報告信息的版本;

看不懂?不要緊,一塊兒來看看怎麼用吧~

image.png-64.4kB

基本用法

awk '{pattern + action}' {filenames}
# 注意,只能用單引號
複製代碼
  • pattern:表示awk在數據中查找的內容;
  • action:表示是在找到的匹配內容時所執行的一系列指令;

這裏來用一個例子展開說明,jb.txt:

Tom is a man who cheats and plays with women's feelings. 
He goes to work every day to fish for fish. 
He lives in a muddle through life.
複製代碼

用法1

awk '{[pattern] action}' {filenames}   
# 行匹配語句 awk '' 只能用單引號
複製代碼

例子:

awk '{print $1,$4}' jb.txt
# 每行按空格或TAB分割,輸出文本中的一、4項

Tom man
He work
He a

# 格式化輸出
awk '{printf "%-8s %-10s\n",$1,$4}' jb.txt

------------------------------------------
Tom      man
He       work
He       a
複製代碼

用法二

awk -F  
#-F至關於內置變量FS, 指定分割字符
複製代碼

例子:

awk -F, '{print $1,$2}'   jb.txt
-----------------------------------
Tom is a man who cheats and plays with women's feelings.
He goes to work every day to fish for fish.
He lives in a muddle through life.

#使用內建變量
awk 'BEGIN{FS=","} {print $1,$2}'     jb.txt
---------------------------------------------
Tom is a man who cheats and plays with women's feelings.
He goes to work every day to fish for fish.
He lives in a muddle through life.

#使用多個分隔符.先使用空格分割,而後對分割結果再使用","分割
awk -F '[ ,]'  '{print $1,$2,$5}'   jb.txt
--------------------------------------------------------
Tom is who
He goes every
He lives muddle
複製代碼

用法三

awk -v  
# 設置變量
複製代碼

例子:

awk -va=1 '{print $1,$1+a}' jb.txt
---------------------------------------------
Tom 1
He 1
He 1

#這裏可能會有疑問,爲何變量+1=1?
原來是由於,將變量經過」+」鏈接運算,自動強制將字符串轉爲整型。
非數字變成0,發現第一個非數字字符,後面自動忽略。 


awk -va=1 -vb=s '{print $1,$1+a,$1b}' jb.txt
---------------------------------------------
Tom 1 Toms
He 1 Hes
He 1 Hes
複製代碼

用法四

awk -f {awk腳本} {文件名}

例子:
awk -f jb.awk log.txt
複製代碼

運算符

運算符 描述
= += -= *= /= %= ^= **= 賦值
?: C條件表達式
\|\| 邏輯或
&& 邏輯與
~ ~! 匹配正則表達式和不匹配正則表達式
< <= > >= != == 關係運算符
空格 鏈接
+ - 加,減
* / % 乘,除與求餘
+ - ! 一元加,減和邏輯非
^ *** 求冪
++ -- 增長或減小,做爲前綴或後綴
$ 字段引用
in 數組成員

jb.txt:

1Tom is a man who cheats and plays with women's feelings. 
3He goes to work every day to fish for fish. 
5He lives in a muddle through life.
複製代碼

過濾第一列大於2的行

awk '$1>2' jb.txt
---------------------------------------
3He goes to work every day to fish for fish.
5He lives in a muddle through life.
複製代碼

過濾第一列等於3的行

awk '$1==3 {print $1,$3}' jb.txt
複製代碼

過濾第一列大於2而且第二列等於'Are'的行

awk '$1>2 && $2=="Are" {print $1,$2,$3}' jb.txt
複製代碼

內建變量

變量 描述
$n 當前記錄的第n個字段,字段間由FS分隔
$0 完整的輸入記錄
ARGC 命令行參數的數目
ARGIND 命令行中當前文件的位置(從0開始算)
ARGV 包含命令行參數的數組
CONVFMT 數字轉換格式(默認值爲%.6g)ENVIRON環境變量關聯數組
ERRNO 最後一個系統錯誤的描述
FIELDWIDTHS 字段寬度列表(用空格鍵分隔)
FILENAME 當前文件名
FNR 各文件分別計數的行號
FS 字段分隔符(默認是任何空格)
IGNORECASE 若是爲真,則進行忽略大小寫的匹配
NF 一條記錄的字段的數目
NR 已經讀出的記錄數,就是行號,從1開始
OFMT 數字的輸出格式(默認值是%.6g)
OFS 輸出記錄分隔符(輸出換行符),輸出時用指定的符號代替換行符
ORS 輸出記錄分隔符(默認值是一個換行符)
RLENGTH 由match函數所匹配的字符串的長度
RS 記錄分隔符(默認是一個換行符)
RSTART 由match函數所匹配的字符串的第一個位置
SUBSEP 數組下標分隔符(默認值是/034)
awk '{print $1,$2,$5}' OFS=" $ "  jb.txt
---------------------------------------
1Tom $ is $ who
3He $ goes $ every
5He $ lives $ muddle

awk '{print NR,FNR,$1,$2,$3}' jb.txt
---------------------------------------
1 1 1Tom is a
2 2 3He goes to
3 3 5He lives in
複製代碼

正則

# 輸出第1列包含 "He",並打印第1列與第2列
awk '$1 ~ /He/ {print $1,$2}' jb.txt
--------------------------------------
3He goes
5He lives

# 輸出包含"is" 的行
awk '/is/ ' jb.txt
---------------------------------------------
1Tom is a man who cheats and plays with women's feelings.
3He goes to work every day to fish for fish.
複製代碼

~ 表示模式開始,// 中是模式。

忽略大小寫

awk 'BEGIN{IGNORECASE=1} /he/' jb.txt
---------------------------------------------
1Tom is a man who cheats and plays with women's feelings.
3He goes to work every day to fish for fish.
5He lives in a muddle through life.
複製代碼

拆分文件

#是按第6例分隔文件,其中的NR!=1表示不處理表頭
awk 'NR!=1{print > $6}' jb.txt

#指定的列輸出到文件
awk 'NR!=1{print $4,$5 > $6}' jb.txt
複製代碼

awk腳本

上面演示的例子,會看到BEGIN跟END的關鍵字,這裏來介紹下:

  • BEGIN{ 這裏面放的是執行前的語句 }
  • END {這裏面放的是處理完全部的行後要執行的語句 }

jb.txt:

Marry   2143 78 84 77
Jack    2321 66 78 45
Tom     2122 48 77 71
Mike    2537 87 97 95
Bob     2415 40 57 62
複製代碼

test.sh:

#!/bin/awk -f
#運行前
BEGIN {
    math = 0
    english = 0
    computer = 0
 
    printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
    printf "---------------------------------------------\n"
}
#運行中
{
    math+=$3
    english+=$4
    computer+=$5
    printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#運行後
END {
    printf "---------------------------------------------\n"
    printf " TOTAL:%10d %8d %8d \n", math, english, computer
    printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}
複製代碼

運行結果:

awk -f test.sh jb.txt

NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL
---------------------------------------------
Marry  2143     78       84       77      239
Jack   2321     66       78       45      189
Tom    2122     48       77       71      196
Mike   2537     87       97       95      279
Bob    2415     40       57       62      159
---------------------------------------------
  TOTAL:       319      393      350
AVERAGE:     63.80    78.60    70.00
複製代碼

工做流程

awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
複製代碼
  • 第一步:執行BEGIN{ commands }語句塊中的語句;
  • 第二步:從文件或標準輸入(stdin)讀取一行,而後執行pattern{ commands }語句塊,它逐行掃描文件,從第一行到最後一行重複這個過程,直到文件所有被讀取完畢。
  • 第三步:當讀至輸入流末尾時,執行END{ commands }語句塊。

更多有關awk詳細的內容,請跳轉到runoobgnu查看,謝謝;

sed

sed是什麼

sed是一款流編輯工具,用來對文本進行過濾與替換工做; sed經過輸入讀取文件內容,但一次僅讀取一行內容進行某些指令處理後輸出,sed更適合於處理大數據文件;

工做原理

sed在處理文本文件的時候,會在內存上建立一個模式空間,而後把這個文件的每一行調入模式空間用相應的命令處理,處理完輸出;接着處理下一行,直到最後;

與vim的區別

vim須要通知處理文件的哪幾行纔會去處理,sed默認會處理文件的全部行,除非你告訴它不處理哪幾行;

語法

sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
複製代碼

options

參數 含義
-e<script>或--expression=<script> 以選項中的指定的script來處理輸入的文本文件;
-f<script文件>或--file=<script文件> 以選項中指定的script文件來處理輸入的文本文件;
-h或--help 顯示幫助;
-n或--quiet或——silent 僅顯示script處理後的結果;
-V或--version 顯示版本信息;

參數 指定待處理的文本文件列表;

命令

參數 含義
a 在當前行下面插入文本;
i 在當前行上面插入文本;
c 把選定的行改成新的文本;
d 刪除,刪除選擇的行;
D 刪除模板塊的第一行;
s 替換指定字符;
h 拷貝模板塊的內容到內存中的緩衝區;
H 追加模板塊的內容到內存中的緩衝區;
g 得到內存緩衝區的內容,並替代當前模板塊中的文本;
G 得到內存緩衝區的內容,並追加到當前模板塊文本的後面;
l 字符的清單;
n 讀取下一個輸入行,用下一個命令處理新的行而不是用第一個命令;
N 追加下一個輸入行到模板塊後面並在兩者間嵌入一個新行,改變當前行號碼;
p 打印模板塊的行;
P(大寫) 打印模板塊的第一行;
q 退出Sed;
b lable 分支到腳本中帶有標記的地方,若是分支不存在則分支到腳本的末尾;
r file 從file中讀行;
t label if分支,從最後一行開始,條件一旦知足或者T,t命令,將致使分支到帶有標號的命令處,或者到腳本的末尾;
T label 錯誤分支,從最後一行開始,一旦發生錯誤或者T,t命令,將致使分支到帶有標號的命令處,或者到腳本的末尾;
w file 寫並追加模板塊到file末尾;
W file 寫並追加模板塊的第一行到file末尾;
! 表示後面的命令對全部沒有被選定的行發生做用;
= 打印當前行號碼;
# 把註釋擴展到下一個換行符之前;

替換標記

參數 含義
g 表示行內全面替;
p 表示打印行;
w 表示把行寫入一個文件;
x 表示互換模板塊中的文本和緩衝區中的文本;
y 表示把一個字符翻譯爲另外的字符(可是不用於正則表達式);
\1 子串匹配標記;
& 已匹配字符串標記;

元字符集

參數 含義
^ 匹配行開始,如:/^sed/匹配全部以sed開頭的行;
$ 匹配行結束,如:/sed$/匹配全部以sed結尾的行;
. 匹配一個非換行符的任意字符,如:/s.d/匹配s後接一個任意字符,最後是d;
* 匹配0個或多個字符,如:/*sed/匹配全部模板是一個或多個空格後緊跟sed的行;
[] 匹配一個指定範圍內的字符,如/[ss]ed/匹配sed和Sed;
[^] 匹配一個不在指定範圍內的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一個字母開頭,緊跟ed的行;
\(..\) 匹配子串,保存匹配的字符,如s/(love)able/\1rs,loveable被替換成lovers;
& 保存搜索字符用來替換其餘字符,如s/love/&/,love這成love
\< 匹配單詞的開始,如:/<love/匹配包含以love開頭的單詞的行。
\> 匹配單詞的結束,如/love>/匹配包含以love結尾的單詞的行;
x\{m\} 重複字符x,m次,如:/0{5}/匹配包含5個0的行;
x\{m,\} 重複字符x,至少m次,如:/0{5,}/匹配至少有5個0的行;
x\{m,n\} 重複字符x,至少m次,很少於n次,如:/0{5,10}/匹配5~10個0的行;

看到這裏,是否懵逼了?

image.png-37.8kB

別看了,直接看實戰吧;

實戰

jb.txt的內容:

jb testing now 
複製代碼

替換文本中的字符串

sed 's/jb/jb2/' jb.txt
--------------------------
jb2 testing now
複製代碼

-n選項和p命令一塊兒使用表示只打印那些發生替換的行:

sed -n 's/jb/jb2/p' jb.txt
複製代碼

直接編輯文件選項-i,會匹配file文件中每一行的第一個jb替換爲jb2:

sed -i 's/jb/jb2/g' jb.txt
複製代碼

image.png-4.6kB

插入
方法1:
用sed的i命令在第一行前面插入便可,加上 -i 選項直接操做文件。
若是不加只是打印不會寫入文件。

例如,文件頭部添加一行字符:

sed -i '1i\3a0000' jb.txt
複製代碼

方法2:
使用-e和-i選項;
jb.txt指定行(好比第三行)後面添加一行內容,好比「3a0000」:

sed -e "/3/a 3a0000" -i jb.txt

例子:

sed -i '2iabc' jb.txt 
複製代碼

在jb.txt文件的第2行的上面插入abc,abc所行爲第2行,原來行變成了第3行!

注意在行數後面的字母i!!!

sed -i '2aabc' jb.txt 
複製代碼

jb.txt文件的第2行的下面插入abc,abc所在行爲第3行,原來行不變任然爲第2行。

注意在行數後面的字母a!!

在a.txt的第88行插入文件b.txt

sed -i '88 r b.file' a.file 

#若是不知道行號,能夠用正則匹配
sed -i '/regex/ r b.txt' a.txt # regex是正則表達式
複製代碼

若是不改變源文件,能夠去掉-i開關,修改會輸出到STDOUT!!

全面替換標記g
使用後綴 /g 標記會替換每一行中的全部匹配:

sed 's/jb/jb2/g' jb.txt
複製代碼

當須要從第N處匹配開始替換時,可使用 /Ng:

$ echo jbjbjbjb | sed 's/jb/jbtest/2g'
jbjbtestjbtestjbtest
複製代碼

定界符
以上命令中字符 / 在sed中做爲定界符使用,也可使用任意的定界符:

sed 's:test:TEXT:g'
sed 's|test|TEXT|g'
複製代碼

定界符出如今樣式內部時,須要進行轉義:

sed 's/\/bin/\/usr\/local\/bin/g'
複製代碼

通常來講,/ :這種經常使用的符號都須要轉義;

刪除操做:d命令

刪除空白行:

sed '/^$/d' jb.txt
複製代碼

刪除文件的第2行:

sed '2d' jb.txt
複製代碼

刪除文件的第2行到末尾全部行:

sed '2,$d' jb.txt
複製代碼

刪除文件最後一行:

sed '$d' jb.txt
複製代碼

刪除文件中全部開頭是test的行:

sed '/^test/'d jb.txt
複製代碼

已匹配字符串標記&
正則表達式 \w+ 匹配每個單詞,使用 [&] 替換它,& 對應於以前所匹配到的單詞:

echo this is a test line | sed 's/\w\+/[&]/g'
---------------------------------------------
[this] [is] [a] [test] [line]
複製代碼

全部以192.168.0.1開頭的行都會被替換成它自已加localhost:

sed 's/^192.168.0.1/&localhost/' jb.txt
192.168.0.1localhost
複製代碼

子串匹配標記\1
匹配給定樣式的其中一部分:

echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
----------------------------------------------------
this is 7 in a number
複製代碼

命令中 digit 7,被替換成了 7。
樣式匹配到的子串是 7,(..) 用於匹配子串,對於匹配到的第一個子串就標記爲 \1,依此類推匹配到的第二個結果就是 \2,例如:

echo aaa BBB | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
---------------------------------------------------------
BBB aaa
複製代碼

love被標記爲1,全部loveable會被替換成lovers,並打印出來:

sed -n 's/\(love\)able/\1rs/p' jb.txt
複製代碼

組合多個表達式

sed '表達式' | sed '表達式'
等價於:
sed '表達式; 表達式'
複製代碼

引用
sed表達式可使用單引號來引用,可是若是表達式內部包含變量字符串,就須要使用雙引號。

test=hello
echo hello WORLD | sed "s/$test/HELLO"
HELLO WORLD
複製代碼

選定行的範圍:,(逗號)
全部在模板test和check所肯定的範圍內的行都被打印:

sed -n '/test/,/check/p' jb.txt
複製代碼

打印從第5行開始到第一個包含以test開始的行之間的全部行:

sed -n '5,/^test/p' jb.txt
複製代碼

對於模板test和west之間的行,每行的末尾用字符串aaa bbb替換:

sed '/test/,/west/s/$/aaa bbb/' jb.txt
複製代碼

多點編輯:e命令
·-e選項容許在同一行裏執行多條命令:

sed -e '1,5d' -e 's/test/check/' jb.txt
複製代碼

上面sed表達式的第一條命令刪除1至5行, 第二條命令用check替換test。

命令的執行順序對結果有影響。
若是兩個命令都是替換命令,那麼第一個替換命令將影響第二個替換命令的結果。

-e 等價的命令是 --expression

sed --expression='s/test/check/' --expression='/love/d' jb.txt
複製代碼

從文件讀入:r命令
file裏的內容被讀進來,顯示在與test匹配的行後面,若是匹配多行,則file的內容將顯示在全部匹配行的下面:

sed '/test/r file' filename
複製代碼

寫入文件:w命令
在example中全部包含test的行都被寫入file裏:

sed -n '/test/w file' example
複製代碼

追加(行下):a命令
將 this is a test line 追加到 以test 開頭的行後面:

sed '/^test/a\this is a test line' jb.txt
複製代碼

在 test.conf 文件第2行以後插入 this is a test line:

sed -i '2a\this is a test line' test.conf
複製代碼

插入(行上):i命令
將 this is a test line 追加到以test開頭的行前面:

sed '/^test/i\this is a test line' jb.txt
複製代碼

在test.conf文件第5行以前插入this is a test line:

sed -i '5i\this is a test line' test.conf
複製代碼

下一個:n命令
若是test被匹配,則移動到匹配行的下一行,替換這一行的aa,變爲bb,並打印該行,而後繼續:

sed '/test/{ n; s/aa/bb/; }' jb.txt
複製代碼

變形:y命令
把1~10行內全部abcde轉變爲大寫,注意,正則表達式元字符不能使用這個命令:

sed '1,10y/abcde/ABCDE/' jb.txt
複製代碼

退出:q命令
打印完第10行後,退出sed

sed '10q' jb.txt
複製代碼

保持和獲取:h命令和G命令
在sed處理文件的時候,每一行都被保存在一個叫模式空間的臨時緩衝區中,除非行被刪除或者輸出被取消,不然全部被處理的行都將 打印在屏幕上。
接着模式空間被清空,並存入新的一行等待處理。

sed -e '/test/h' -e '$G' jb.txt
複製代碼

在這個例子裏,匹配test的行被找到後,將存入模式空間,h命令將其複製並存入一個稱爲保持緩存區的特殊緩衝區內。
第二條語句的意思是,當到達最後一行後,G命令取出保持緩衝區的行,而後把它放回模式空間中,且追加到如今已經存在於模式空間中的行的末尾。
在這個例子中就是追加到最後一行。
簡單來講,任何包含test的行都被複制並追加到該文件的末尾。

保持和互換:h命令和x命令
互換模式空間和保持緩衝區的內容。
也就是把包含test與check的行互換:

sed -e '/test/h' -e '/check/x' file
複製代碼

腳本scriptfile
sed腳本是一個sed的命令清單,啓動Sed時以-f選項引導腳本文件名。
Sed對於腳本中輸入的命令很是挑剔,在命令的末尾不能有任何空白或文本,若是在一行中有多個命令,要用分號分隔。
#開頭的行爲註釋行,且不能跨行。

sed [options] -f scriptfile file(s)
複製代碼

打印奇數行或偶數行
方法1:

sed -n 'p;n' test.txt  #奇數行
sed -n 'n;p' test.txt  #偶數行
複製代碼

方法2:

sed -n '1~2p' test.txt  #奇數行
sed -n '2~2p' test.txt  #偶數行
複製代碼

打印匹配字符串的下一行

grep -A 1 SCC URFILE
sed -n '/SCC/{n;p}' URFILE
awk '/SCC/{getline; print}' URFILE
複製代碼

總的來講,sed 更適合編輯匹配到的文本

xargs

xargs是什麼

xargs 是給命令傳遞參數的一個過濾器,也是組合多個命令的一個工具。

能夠將輸入內容(一般經過命令行管道傳遞),轉成後續命令的參數,一般用途有:

  • 命令組合:尤爲是一些命令不支持管道輸入,好比ls。
  • 避免參數過長:xargs能夠經過-nx來將參數分組,避免參數過長。

之因此能用到這個命令,關鍵是因爲不少命令不支持|管道來傳遞參數,而平常工做中有有這個必要,因此就有了 xargs 命令:

find /sbin -perm +700 |ls -l       #這個命令是錯誤的
find /sbin -perm +700 |xargs ls -l   #這樣纔是正確的
複製代碼

xargs 通常是和管道一塊兒使用;

語法

somecommand |xargs -item  command
複製代碼

參數

參數 說明
-0 當 stdin 含有特殊子元的時候,將其當成通常字符;
-a file 從文件中讀入做爲sdtin;
-e flag flag必須是一個以空格分隔的標誌,當xargs分析到含有flag這個標誌的時候就中止;
-p 當每次執行一個argument的時候詢問一次用戶;
-n num 後面加次數,表示命令在執行的時候一次用的argument的個數,默認是用全部的;
-t 表示先打印命令,而後再執行;
-i 或-I 將xargs的每項名稱,通常是一行一行賦值給 {},能夠用 {} 代替;
-r no-run-if-empty 當xargs的輸入爲空的時候則中止xargs,不用再去執行了;
-s num 命令行的最大字符數,指的是 xargs 後面那個命令的最大命令行字符數;
-L num 從標準輸入一次讀取 num 行送給 command 命令;
-d delim 分隔符,默認的xargs分隔符是回車,argument的分隔符是空格,這裏修改的是xargs的分隔符;
-x exit的意思,主要是配合-s使用;
-P 修改最大的進程數,默認是1;

實戰

定義一個測試文件,內有多行文本數據:

# cat test.txt

a b c d e f g
h i j k l m n
o p q
r s t
u v w x y z
複製代碼

多行輸入單行輸出

# cat test.txt | xargs
---------------------------------------------------
a b c d e f g h i j k l m n o p q r s t u v w x y z
複製代碼

-n 選項多行輸出

# cat test.txt | xargs -n3
---------------------------------
a b c
d e f
g h i
j k l
m n o
p q r
s t u
v w x
y z
複製代碼

-d 選項能夠自定義一個定界符

# echo "nameXnameXnameXname" | xargs -dX
------------------------------------------
name name name name
複製代碼

結合 -n 選項使用

# echo "nameXnameXnameXname" | xargs -dX -n2
------------------------------------------
name name
name name
複製代碼

讀取 stdin,將格式化後的參數傳遞給命令; 假設一個命令爲 sk.sh 和一個保存參數的文件 arg.txt:

#!/bin/bash
#sk.sh命令內容,打印出全部參數。

echo $*
複製代碼

arg.txt文件內容:

# cat arg.txt
------------------------------------------
aaa
bbb
ccc
複製代碼

xargs 的一個選項 -I,使用 -I 指定一個替換字符串 {},這個字符串在 xargs 擴展時會被替換掉,當 -I 與 xargs 結合使用,每個參數命令都會被執行一次:

# cat arg.txt | xargs -I {} ./sk.sh -p {} -l
-------------------------------------------
-p aaa -l
-p bbb -l
-p ccc -l
複製代碼

複製全部圖片文件到 /data/images 目錄下

ls *.jpg | xargs -n1 -I cp {} /data/images
複製代碼

xargs 結合 find 使用

用 rm 刪除太多的文件時候,可能獲得一個錯誤信息:/bin/rm Argument list too long. 用 xargs 去避免這個問題:

find . -type f -name "*.log" -print0 | xargs -0 rm -f
複製代碼

xargs -0 將 \0 做爲定界符; 統計一個源代碼目錄中全部 php 文件的行數

find . -type f -name "*.php" -print0 | xargs -0 wc -l
複製代碼

查找全部的 jpg 文件,而且壓縮它們

find . -type f -name "*.jpg" -print | xargs tar -czvf images.tar.gz
複製代碼

xargs 其餘應用

假如你有一個文件包含了不少你但願下載的 URL,你可以使用 xargs下載全部連接:

# cat url-list.txt | xargs wget -c
複製代碼

curl

curl命令是個功能強大的網絡工具,支持經過http、ftp等方式下載文件、上傳文件。
還能夠用來抓取網頁、網絡監控等方面的開發,解決開發過程當中遇到的問題。

經常使用參數

curl命令參數不少,這裏只列出曾經用過、特別是在shell腳本中用到過的那些。

參數 說明
-v/--verbose 小寫的v參數,用於打印更多信息,包括髮送的請求信息,這在調試腳本是特別有用;
-m/--max-time 指定處理的最大時長;
-H/--header
指定請求頭參數;
-s/--slient 減小輸出的信息,好比進度;
--connect-timeout 指定嘗試鏈接的最大時長;
-x/--proxy <proxyhost[:port]> 指定代理服務器地址和端口,端口默認爲1080;
-T/--upload-file 指定上傳文件路徑;
-o/--output 指定輸出文件名稱;
-d/--data/--data-ascii 指定POST的內容;
--retry 指定重試次數;
-e/--referer 指定引用地址;
-I/--head 僅返回頭部信息,使用HEAD請求;

實戰

curl安裝

sudo apt-get install curl
複製代碼

GET請求

curl http://www.baidu.com
#回車以後,HTML內容打印在屏幕上;
#若是這裏的URL指向的是一個文件或者一幅圖均可以直接下載到本地;

curl -i "http://www.baidu.com"  
#顯示所有信息

curl -l "http://www.baidu.com" 
#只顯示頭部信息

curl -v "http://www.baidu.com" 
#顯示get請求全過程解析
複製代碼

下載
保存網頁:

curl -o baidu.html http://www.baidu.com
複製代碼

下載一個圖片

curl -o girl.jpg http://hostname.com/girl.jpg
複製代碼

若是想下載圖片的名字和服務器保持一致 -O 大寫的O

curl -O http://hostname.com/girl.jpg
複製代碼

能夠看到屏幕上出現一個下載頁面進度指示,等到100%,就保存完成了.

上傳

-T/--upload-file:往服務器上傳文件
複製代碼

用法:

#上傳多個文件
curl -T "img[1-1000].png" ftp://example.com/upload/

#上傳多個文件
curl -T "{file1,file2}" http://www.example.com
複製代碼

GET請求
帶參:

curl http://www.xxxx.com/getDataList?param1=value1&param2=value2
複製代碼

POST方法
-d或--data參數
post請求,用法:

curl -d "id=1&name=test" http://example.com/example.php
#需把請求的參數和URL分開
複製代碼

同時可使用:

curl -d "id=1" -d "name=test" http://example.com/example.php
#至關於提交了兩個參數。
複製代碼

也能夠指定一個文件,將該文件中的內容看成數據傳遞給服務器端

curl --data @filename https://hostname.com/xxx
複製代碼

當提交的參數值中有特殊字符就須要先轉義。
如空格時,就須要轉義成%20。

curl -d "value%201" http://hostname.com
複製代碼

--data-urlencode參數
能夠自動轉義成特殊字符,無需人工事先轉義。

curl --data-urlencode "name=April 1" http://example.com/example.php
複製代碼

-F或--form
將本地文件上傳到服務器,用法爲:

curl -F "filename=@/home/test/test.pic" http://example.com/example.php 
#千萬不能漏掉@符號。
複製代碼

設置referer
有時候咱們若是直接請求某個URL不能成功,它須要判斷referer是否正確,那就能夠經過-e或--referer參數模擬

curl --referer http://www.example.com http://www.example.com
複製代碼

指定User Agent
-A/--user-agent
假裝成指定的瀏覽器Chrome訪問

用法:

curl -A "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" www.baidu.com
複製代碼

僞造cookie
-b或--cookie:
有兩種用法,一是指定參數和值:

curl --cookie "name=xxx" http://www.example.com 二是從文件讀取:
curl -b /cookie.txt http://www.example.com
複製代碼

保存cookie
-c/--cookie-jar
curl命令執行後保存操做時生成的cookie到文件:

curl -c ./cookie.txt -d username=aaaa -d pwd=****** http://www.example.com 
複製代碼

將網站的cookies信息保存到sugarcookies文件中

curl -D cookies.txt http://localhost/sugarcrm/index.php
複製代碼

定義輸出顯示內容
-w/--write-out:
能夠定義輸出的內容,如經常使用的http碼,tcp鏈接時間,域名解析的時間,握手時間及第一時間響應時間等,很是強大。

一、打印出返回的http碼

curl -o /dev/null -s -w %{http_code} "http://www.baidu.com" 
複製代碼

二、打印響應時間

curl -o /dev/null -s -w "time_total: %{time_total}\n" "http://www.baidu.com"
複製代碼

CURL受權
在訪問須要受權的頁面時,可經過-u選項提供用戶名和密碼進行受權

curl -u username:password URL
複製代碼

一般的作法是在命令行只輸入用戶名,以後會提示輸入密碼,這樣能夠保證在查看歷史記錄時不會將密碼泄露

curl -u username URL
複製代碼

封裝請求

#!/bin/bash
function httpRequest()
{
    #curl 請求
    info=`curl -s -m 10 --connect-timeout 10 -I $1`
 
    #獲取返回碼
    code=`echo $info|grep "HTTP"|awk '{print $2}'`
    #對響應碼進行判斷
    if [ "$code" == "200" ];then
        echo "請求成功,響應碼是$code"
    else
        echo "請求失敗,響應碼是$code"
    fi
}
 
httpRequest "$1"
複製代碼

執行結果:

image.png-15.8kB

小結

呼,終於搞定了,不容易,但願能幫助你快速瞭解shell;

本章從什麼是shell開始,講解變量、註釋、字符串及操做、數組、傳參、管道、條件/流程判斷、以及經常使用的ps\grep\sed等命令簡單介紹;

目的只是但願能快速瞭解shell,否則懵逼半天,簡單看完後留個大概印象,好歹再次見面就知道什麼了;

最後,謝謝你們~

1-140R3154U8.jpg-9kB
相關文章
相關標籤/搜索