linux系列之shell程式設計(一)
shell概述
shell是一個指令行解釋器,它接收應用程式/使用者指令,然後調用作業系統核心。
<!--more-->
shell解釋器
- Linux提供的解釋器有
[shaofei@upuptop-pc ~]$ cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
複制
- bash和sh的關系
[shaofei@upuptop-pc bin]$ ll | grep bash
-rwxr-xr-x 1 root root 964600 Aug 8 2019 bash
lrwxrwxrwx 1 root root 4 Oct 28 2019 sh -> bash
複制
- Centos預設的解析器是bash
[shaofei@upuptop-pc bin]$ echo $SHELL
/bin/bash
複制
Shell腳本入門
-
腳本格式
腳本以 #!/bin/bash 開頭(指定解析器)
- 第一個shell腳本
[shaofei@upuptop-pc sh]$ touch helloworld.sh
[shaofei@upuptop-pc sh]$ vim helloworld.sh
#!/bin/bash
echo "helloworld"
複制
- 腳本的常用執行方式
(1)
采用bash或sh+腳本的相對路徑或絕對路徑(不用賦予腳本+x權限)
[shaofei@upuptop-pc sh]$ sh helloworld.sh
helloworld
[shaofei@upuptop-pc sh]$ bash helloworld.sh
helloworld
複制
(2)
采用輸入腳本的絕對路徑或相對路徑執行腳本(必須具有可執行權限+x)
[shaofei@upuptop-pc sh]$ chmod 777 helloworld.sh
[shaofei@upuptop-pc sh]$ ./helloworld.sh
helloworld
[shaofei@upuptop-pc sh]$ /home/shaofei/sh/helloworld.sh
helloworld
複制
注意:第一種執行方法,本質是bash解析器幫你執行腳本,是以腳本本身不需要執行權限。第二種執行方法,本質是腳本需要自己執行,是以需要執行權限。
- 多指令處理
[shaofei@upuptop-pc sh]$ touch batch.sh
[shaofei@upuptop-pc sh]$ vim batch.sh
#!/bin/bash
echo 'hello'
cd /home/shaofei/sh
echo 'cccc' > a.txt
複制
Shell中的變量
系統變量
- 常用的系統變量
$PWD,$HOME,$USER,$SHELL等
- 案例
[shaofei@upuptop-pc sh]$ echo $HOME
/home/shaofei
[shaofei@upuptop-pc sh]$ echo $PWD
/home/shaofei/sh
[shaofei@upuptop-pc sh]$ echo $USER
shaofei
顯示目前Shell中所有變量:set
[shaofei@upuptop-pc sh]$ set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
………………
複制
自定義變量
-
基本文法
a. 定義變量: 變量名=變量值
b. 撤銷變量: unset 變量名
c. 聲明靜态變量: readonly 變量, 注意不能unset
-
定義規則
a. 變量名可以使用字母、數字、下劃線組成,但是不能以數字開頭。環境變量建議全部大寫
b. <font color=red>等号前後不能有空格</font>
c. 在bash中,變量類型預設是字元串類型,無法直接進行數值計算
d. 變量的值如果有空格必須要用"雙引号"引起來
- 案例
建立變量A并指派為5
[shaofei@upuptop-pc sh]$ A=5
[shaofei@upuptop-pc sh]$ echo $A
5
給變量A重新指派為9
[shaofei@upuptop-pc sh]$ A=9
[shaofei@upuptop-pc sh]$ echo $A
9
撤銷變量A
[shaofei@upuptop-pc sh]$ unset A
[shaofei@upuptop-pc sh]$ echo $A
建立靜态的變量B
[shaofei@upuptop-pc sh]$ readonly B=2
[shaofei@upuptop-pc sh]$ echo $B
2
靜态變量不能重新指派
[shaofei@upuptop-pc sh]$ B=10
-bash: B: readonly variable
靜态變量不能unset
[shaofei@upuptop-pc sh]$ unset B
-bash: unset: B: cannot unset: readonly variable
在bash中,變量預設類型都是字元串類型,無法直接進行數值運算
[shaofei@upuptop-pc sh]$ C=1+2
[shaofei@upuptop-pc sh]$ echo $C
1+2
變量的值如果有空格,需要使用雙引号或單引号括起來
[shaofei@upuptop-pc sh]$ D=I LOVE YOU
-bash: LOVE: command not found
[shaofei@upuptop-pc sh]$ D="I LOVE YOU"
[shaofei@upuptop-pc sh]$ echo $D
I LOVE YOU
可把變量提升為全局環境變量,可供其他Shell程式使用
[shaofei@upuptop-pc sh]$ vim helloworld.sh
在helloworld.sh檔案中增加echo $B
#!/bin/bash
echo "helloworld"
echo $B
沒有列印$B的值
[shaofei@upuptop-pc sh]$ sh helloworld.sh
helloworld
修改B變量為全局環境變量
[shaofei@upuptop-pc sh]$ export B
[shaofei@upuptop-pc sh]$ sh helloworld.sh
helloworld
2
複制
特殊變量:$n
- 基本文法
$n
功能描述:n為數字,$0 代表該腳本名稱,$1-$9代表第一到第九個參數,十以内的參數,十以上的參數需要用大括号包含,如${10}
- 案例
輸出該腳本的檔案名稱、輸入參數1和輸入參數2的值
[shaofei@upuptop-pc sh]$ touch param.sh
[shaofei@upuptop-pc sh]$ vim param.sh
#!/bin/bash
echo $0 $1 $2
[shaofei@upuptop-pc sh]$ sh param.sh 1 2 3
param.sh 1 2
複制
特殊變量:$
- 基本文法
$# (擷取所有的參數個數,常用于循環)
- 案例
[shaofei@upuptop-pc sh]$ vim param.sh
#!/bin/bash
echo $#
[shaofei@upuptop-pc sh]$ sh param.sh 1 2 3 4 5
5
複制
特殊變量: $*
、 $@
$*
$@
- 基本說法
$*
(功能描述:這個變量代表指令行中所有的參數,
$*
把所有的參數看做一個整體)
$@
(功能描述: 這個變量代表指令行中所有的參數,不過
$@
把每個參數差別對待)
- 案例
[shaofei@upuptop-pc sh]$ vim param.sh
#!/bin/bash
echo $@
echo $*
[shaofei@upuptop-pc sh]$ sh param.sh 1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
複制
特殊變量:$?
- 基礎文法
$? (功能描述:最後一次執行的指令的傳回狀态。如果這個變量的值為0,證明上一個指令正确執行;如果這個變量的值為非0,則證明上一個指令執行不正确了)
- 示例:
[shaofei@upuptop-pc sh]$vim param.sh
#!/bin/bash
echo $?
[shaofei@upuptop-pc sh]$ ./helloworld.sh
helloworld
[shaofei@upuptop-pc sh]$ sh param.sh
0
複制
運算符
- 基礎文法
(1)
$((運算式))
或
$[運算式]
(2)
expr +,-,*,/,%
加,減,乘,除,取餘
注意:expr 運算符之間要有空格
-
執行個體
(1)計算3+2的值
[shaofei@upuptop-pc sh]$ expr 3 + 2
5
複制
(2)計算3-2的值
[shaofei@upuptop-pc sh]$ expr 3 - 2
1
複制
(3)計算(2+3)* 4的值
第一種方式
[shaofei@upuptop-pc sh]$ expr `expr 2 + 3 ` \* 4
20
第二種方式
[shaofei@upuptop-pc sh]$ echo $(((3+2)*4))
20
第三種方式
[shaofei@upuptop-pc sh]$ echo $[(2+3)*4]
20
複制
條件判斷
- 基本文法
(注意:condition前後有空格)[ condition ]
- 常用的判斷條件
(1) 兩個整數之間比較
=
字元串比較
-lt
小于(less than)
-le
小于等于(less equal)
-eq
等于(equal)
-gt
大于(greater)
-ge
大于等于(greater equal)
-ne
不等于(Not equal)
(2) 按照檔案權限進行比較
-r
有讀的權限(read)
-w
有寫的權限(write)
-x
有執行的權限(execute)
(3) 按照檔案類型進行判斷
-f
檔案存在并且是一個正常的檔案(file)
-e
檔案存在(existence)
-d
檔案存在且是一個目錄(directory)
案例:
- 23 是否大于等于 22
[shaofei@upuptop-pc ~]$ [ 22 -ge 23 ]
[shaofei@upuptop-pc ~]$ echo $?
1
[shaofei@upuptop-pc ~]$ [ 23 -ge 23 ]
[shaofei@upuptop-pc ~]$ echo $?
0
複制
- HelloWorld.sh 是否有寫的權限
-rw-rw-r-- 1 shaofei shaofei 5 May 8 23:02 a.txt
-rw-rw-r-- 1 shaofei shaofei 65 May 8 23:01 batch.sh
-rwxrwxrwx 1 shaofei shaofei 38 May 8 23:36 helloworld.sh
-rw-rw-r-- 1 shaofei shaofei 31 Dec 8 01:01 k.sh.template
-rw-rw-r-- 1 shaofei shaofei 22 May 9 21:56 param.sh
-rw-rw-r-- 1 shaofei shaofei 59 Dec 8 01:01 start.sh.template
-rwxrwxrwx 1 shaofei shaofei 21 Nov 20 09:58 test1.sh
[shaofei@upuptop-pc sh]$ [ -r helloworld.sh ]
[shaofei@upuptop-pc sh]$ echo $?
0
[shaofei@upuptop-pc sh]$ [ -x batch.sh ]
[shaofei@upuptop-pc sh]$ echo $?
1
複制
- /home/shaofei/aaa.txt 是否存在
[shaofei@upuptop-pc sh]$ [ -e /home/shaofei/aaa.txt ]
[shaofei@upuptop-pc sh]$ echo $?
1
複制
- 多條件判斷(&& 表示前一條指令執行成功時,才執行後一條指令,|| 表示上一條指令執行失敗後,才執行下一條指令)
[shaofei@upuptop-pc sh]$ [ -e /home/shaofei/aaa.txt ] || echo false
false
[shaofei@upuptop-pc sh]$ [ -e /home/shaofei/aaa.txt ] && echo false
[shaofei@upuptop-pc sh]$
複制
流程語句(重點)
if判斷
- 基本文法
if [ 條件判斷式 ]; then
程式代碼
fi
複制
或者
if [ 條件判斷式 ]
then
程式代碼
fi
複制
注意:
-
中括号和條件判斷式之間必須有空格[ 條件表達式 ]
-
後面要有空格if
- 第一種方式
前面要有分号then
- 案例
輸入一個數字,如果是1 則輸出 true 如果是2 則輸出 false 如果是其他數字則不做任何操作
[shaofei@upuptop-pc sh]$ vim if.sh
#!/bin/bash
if [ $1 -eq 1 ]; then
echo true
fi
if [ $1 -eq 2 ]; then
echo false
fi
[shaofei@upuptop-pc sh]$ sh if.sh 1
true
[shaofei@upuptop-pc sh]$ sh if.sh 2
false
[shaofei@upuptop-pc sh]$ sh if.sh 123
[shaofei@upuptop-pc sh]$
複制
case語句
- 基礎文法
case $變量名 in
"value1")
如果變量等于value1,執行程式
;;
"value2")
如果變量等于value2,執行程式
;;
……省略其他分支……
esac
複制
注意
- case行尾必須為單詞
,每一個模式比對必須以in
結束。)
- 雙分号
表示指令序列結束,相當于;;
中的java
break
- 最後可以使用
表示預設模式,相當于*)
中的java
break
- 最後以
結束esac
- 案例
輸入一個數字,如果是1 則輸出 true 如果是2 則輸出 false 如果是其他數字輸出default
[shaofei@upuptop-pc sh]$ vim case.sh
#!/bin/bash
case $1 in
1)
echo true
;;
2)
echo false
;;
*)
echo default
;;
esac
[shaofei@upuptop-pc sh]$ sh case.sh 1
true
[shaofei@upuptop-pc sh]$ sh case.sh 2
false
[shaofei@upuptop-pc sh]$ sh case.sh 3
default
[shaofei@upuptop-pc sh]$
複制
for循環
- 基本文法
- 第一種方式
for (( 初始值;循環控制條件;變量變化 ))
do
程式
done
複制
- 第二種方式
for 變量 in 變量1,變量2,變量
do
程式
done
複制
- 執行個體
計算1-100的和
[shaofei@upuptop-pc sh]$ vim for1.sh
#!/bin/bash
sum=0
for ((i=1;i<=100;i++))
do
sum=$[$sum+$i] # or sum=$(( $sum+$i ))
done
echo $sum
[shaofei@upuptop-pc sh]$ sh for1.sh
複制
列印所有的輸入參數 比較$* 和 $@
- 當
和$*
都不被雙引号$@
包括的時候,沒有差別,""
和$*
都表示傳遞給函數或腳本的所有參數,不被雙引号$@
包含時,都以""
的形式輸出所有參數。$1 $2 …$n
[shaofei@upuptop-pc sh]$ vim for2.sh
#!/bin/bash
echo ---------$*
for i in $*
do
echo $i
done
echo --------$#
for j in $@
do
echo $j
done
echo --------end
[shaofei@upuptop-pc sh]$ sh for2.sh 1 2 3 4
---------1 2 3 4
1
2
3
4
--------4
1
2
3
4
--------end
複制
- 當它們被雙引号
包含時,""
會将所有的參數作為一個整體,以"$*"
的形式輸出所有參數;"$1 $2 …$n"
會将各個參數分開,以"$@"
的形式輸出所有參數。"$1" "$2"…"$n"
[shaofei@upuptop-pc sh]$ vim for3.sh
#!/bin/bash
echo ---------"$*"
for i in "$*"
do
echo $i
done
echo --------$#
for j in "$@"
do
echo $j
done
echo --------end
[shaofei@upuptop-pc sh]$ sh for3.sh 1 2 3 4
---------1 2 3 4
1 2 3 4
--------4
1
2
3
4
--------end
複制
while循環
- 基本文法
while [ 條件表達式 ]
do
程式
done
複制
- 案例
計算1-100的和
[shaofei@upuptop-pc sh]$ vim while.sh
#!/bin/bash
sum=0
i=0
while [ $i -le 100 ]
do
sum=$(( $sum+$i ))
i=$[$i+1]
done
echo $sum
[shaofei@upuptop-pc sh]$ sh while.sh
5050
複制
注意: while後面有空格.
read(讀取使用者輸入)
- 基本文法
read(選項)(參數)
選項:
-p:指定讀取值時的提示符;
-t:指定讀取值時等待的時間(秒)。
參數
變量:指定讀取值的變量名
複制
- 執行個體
[shaofei@upuptop-pc sh]$ vim read.sh
#!/bin/bash
read -p "input your name: " -t 3 NAME
echo "Your Name is $NAME !"
[shaofei@upuptop-pc sh]$ sh read.sh
input your name: shaofeer
Your Name is shaofeer !
複制
函數
系統函數
- basename
- basename基本文法
basename [string/pathname][suffix]
(功能描述:basename指令會删掉所有的字首包括最後一個(‘/’)字元,然後将字元串顯示出來。
選項:
suffix為字尾,如果suffix被指定了,basename會将pathname或string中的suffix去掉。
複制
- 案例實操
[shaofei@upuptop-pc sh]$ basename /home/shaofei/123.txt
123.txt
[shaofei@upuptop-pc sh]$ basename /home/shaofei/123.txt .txt
123
複制
- dirname
- dirname基本文法
dirname 檔案絕對路徑
(功能描述:從給定的包含絕對路徑的檔案名中去除檔案名(非目錄的部分),然後傳回剩下的路徑(目錄的部分))
複制
- 案例實操
擷取a.txt檔案的路徑
[shaofei@upuptop-pc sh]$ dirname /home/shaofei/sh/a.txt
/home/shaofei/sh
複制
自定義函數
- 基本文法
[ function ] funname[()]
{
Action;
[return int;]
}
funname
複制
-
經驗技巧
-(1)必須在調用函數地方之前,先聲明函數,shell腳本是逐行運作。不會像其它語言一樣先編譯。
-(2)函數傳回值,隻能通過$?系統變量獲得,可以顯示加:return傳回,如果不加,将以最後一條指令運作結果,作為傳回值。return後跟數值n(0-255)
3.案例實操
(1)計算兩個輸入參數的和
複制
[shaofei@upuptop-pc sh]$ vim fun.sh
#!/bin/bash
function sum(){
sum=$[$1+$2]
return $sum
}
sum 1 2
echo $?
[shaofei@upuptop-pc sh]$ sh fun.sh
3
複制
shell工具(重點)
cut
cut的工作就是“剪”,具體的說就是在檔案中負責剪切資料用的。cut 指令從檔案的每一行剪切位元組、字元和字段并将這些位元組、字元和字段輸出。
- 基本用法
cut[選項參數] filename
說明:預設分隔符是制表符
複制
- 參數說明
-f
列号,提取第幾列
-d
分隔符,按照指定分隔符分割列
- 執行個體
準備資料
[shaofei@upuptop-pc sh]$ touch txt
[shaofei@upuptop-pc sh]$ vim txt
hello world
java andorid python shell spark nodejs vue
1.切出第一列
[shaofei@upuptop-pc sh]$ cut -d " " -f 1 txt
hello
java
2.切割cut.txt第一、三列
[shaofei@upuptop-pc sh]$ cut -d " " -f 1,3 txt
hello
java python
3.切割cut.txt第二、三列
[shaofei@upuptop-pc sh]$ cut -d " " -f 2,3 txt
world
andorid python
4.在cut.txt檔案中切割出java
[shaofei@upuptop-pc sh]$ cat txt | grep java | cut -d " " -f 1
java
複制
sed
sed是一種流編輯器,它一次處理一行内容。處理時,把目前處理的行存儲在臨時緩沖區中,稱為“模式空間”,接着用sed指令處理緩沖區中的内容,處理完成後,把緩沖區的内容送往螢幕。接着處理下一行,這樣不斷重複,直到檔案末尾。檔案内容并沒有改變,除非你使用重定向存儲輸出。
- 基本用法sed [選項參數] 'command' filename
- 選項參數說明
參數 | 說明 |
---|---|
-e | 直接在指令列模式上進行sed的動作編輯。 |
- 指令功能描述
參數 | 說明 |
---|---|
a | 新增,a的後面可以接字串,在下一行出現 |
d | 删除 |
s | 查找并替換 |
-
案例實操
(0)資料準備
[shaofei@upuptop-pc sh]$ touch sed.txt
[shaofei@upuptop-pc sh]$ vim sed.txt
dong shen
guan zhen
wo wo
lai lai
le le
将“shaofeer”這個單詞插入到sed.txt第二行下,列印。
[shaofei@upuptop-pc sh]$ sed '2a shaofeer' sed.txt
dong shen
guan zhen
shaofeer
wo wo
lai lai
le le
源檔案并沒有變化
[shaofei@upuptop-pc sh]$ cat sed.txt
dong shen
guan zhen
wo wo
lai lai
le le
删除sed.txt檔案所有包含wo的行
[shaofei@upuptop-pc sh]$ sed '/wo/d' sed.txt
dong shen
guan zhen
lai lai
le le
将sed.txt檔案中wo替換為ni
[shaofei@upuptop-pc sh]$ sed 's/wo/ni/g' sed.txt
dong shen
guan zhen
ni ni
lai lai
le le
注意:‘g’表示global,全部替換
複制
awk
一個強大的文本分析工具,把檔案逐行的讀入,以空格為預設分隔符将每行切片,切開的部分再進行分析處理。
- 基本用法
awk [選項參數] 'pattern1{action1} pattern2{action2}...' filename
pattern:表示AWK在資料中查找的内容,就是比對模式
action:在找到比對内容時所執行的一系列指令
複制
- 選項參數說明
選項參數 | 功能 |
---|---|
-F | 指定輸入檔案折分隔符 |
-v | 指派一個使用者定義變量 |
- 案例實操
資料準備
[shaofei@upuptop-pc sh]$ sudo cp /etc/passwd ./
(1)搜尋passwd檔案以root關鍵字開頭的所有行,并輸出該行的第7列。
[shaofei@upuptop-pc sh]$ awk -F: '/^root/{print $7}' passwd
/bin/bash
(2)搜尋passwd檔案以root關鍵字開頭的所有行,并輸出該行的第1列和第7列,中間以“,”号分割。
[shaofei@upuptop-pc sh]$ awk -F: '/^root/{print $1","$7}' passwd
root,/bin/bash
注意:隻有比對了pattern的行才會執行action
(3)隻顯示/etc/passwd的第一列和第七列,以逗号分割,且在所有行前面添加列名user,shell在最後一行添加"dahaige,/bin/zuishuai"。
[shaofei@upuptop-pc sh]$ awk -F : 'BEGIN{print "user, shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd
user, shell
root,/bin/bash
bin,/sbin/nologin
……
www,/sbin/nologin
dahaige,/bin/zuishuai
注意:BEGIN 在所有資料讀取行之前執行;END 在所有資料執行之後執行。
(4)将passwd檔案中的使用者id增加數值1并輸出
[shaofei@upuptop-pc sh]$ awk -v i=1 -F: '{print $3+i}' passwd
1
2
3
4
5
6
7
8
9
複制
- awk的内置變量
變量 | 說明 |
---|---|
FILENAME | 檔案名 |
NR | 已讀的記錄數 |
NF | 浏覽記錄的域的個數(切割後,列的個數) |
- 案例實操
(1)統計passwd檔案名,每行的行号,每行的列數
[shaofei@upuptop-pc sh]$ awk -F: '{print "filename:" FILENAME ", linenumber:" NR ",columns:" NF}' passwd
filename:passwd, linenumber:1,columns:7
filename:passwd, linenumber:2,columns:7
filename:passwd, linenumber:3,columns:7
filename:passwd, linenumber:4,columns:7
filename:passwd, linenumber:5,columns:7
filename:passwd, linenumber:6,columns:7
(3)查詢sed.txt中空行所在的行号
[shaofei@upuptop-pc sh]$ awk '/^$/{print NR}' sed.txt
5
複制
sort
sort指令是在Linux裡非常有用,它将檔案進行排序,并将排序結果标準輸出。
-
基本文法
sort(選項)(參數)
選項 | 說明 |
---|---|
-n | 依照數值的大小排序 |
-r | 以相反的順序來排序 |
-t | 設定排序時所用的分隔字元 |
-k | 指定需要排序的列 |
參數:指定待排序的檔案清單
-
案例實操
(0)資料準備
[shaofei@upuptop-pc sh]$ vim sort.txt
bb:40:5.4
bd:20:4.2
xz:50:2.3
cls:10:3.5
ss:30:1.6
按照“:”分割後的第三列倒序排序。
[shaofei@upuptop-pc sh]$ sort -t : -nrk 3 sort.txt
bb:40:5.4
bd:20:4.2
cls:10:3.5
xz:50:2.3
ss:30:1.6
複制
面試題
-
問題1:使用Linux指令查詢file1中空行所在的行号
答案:
[shaofei@upuptop-pc sh]$ awk '/^$/{print NR}' sed.txt
5
複制
- 問題2:有檔案chengji.txt内容如下:
張三 40
李四 50
王五 60
使用Linux指令計算第二列的和并輸出
[shaofei@upuptop-pc sh]$ cat chengji.txt | awk -F " " '{sum+=$2} END{print sum}'
150
複制
- 問題3:Shell腳本裡如何檢查一個檔案是否存在?如果不存在該如何處理?
#!/bin/bash
if [ -f file.txt ]; then
echo "檔案存在!"
else
echo "檔案不存在!"
fi
複制
- 問題4:用shell寫一個腳本,對文本中無序的一列數字排序
[shaofei@upuptop-pc sh]$ cat test.txt
9
8
7
6
5
4
3
2
10
1
[shaofei@upuptop-pc sh]$ sort -n test.txt|awk '{a+=$0;print $0}END{print "SUM="a}'
1
2
3
4
5
6
7
8
9
10
SUM=55
複制
- 問題5:請用shell腳本寫出查找目前檔案夾(/home)下所有的文本檔案内容中包含有字元”shen”的檔案名稱
[shaofei@upuptop-pc sh]$ grep -r "shen" /home | cut -d ":" -f 1
/home/shaofeer/datas/sed.txt
/home/shaofeer/datas/cut.txt
複制
結尾
好記性不如爛筆頭,本文為個人學習總結。