前言
為什么學 Shell
Shell 腳本語言是實現 Linux/UNIX 系統管理及自動化運維所必備的重要工具, Linux/UNIX 系統的底層及基礎應用軟件的核心大都涉及 Shell 腳本的內容。每一個合格 的Linux 系統管理員或運維工程師,都需要能夠熟練地編寫 Shell 腳本語言,并能夠閱讀系統及各類軟件附帶的 Shell 腳本內容。只有這樣才能提升運維人員的工作效率,適 應曰益復雜的工作環境,減少不必要的重復工作,從而為個人的職場發展奠定較好的基礎
什么是 shell
Shell 是一個命令解釋器,它在操作系統的最外層,負責直接與用戶對話,把用戶的輸入解釋給操作系統,并處理各種各樣的操作系統的輸出結果,輸出屏幕返回給用戶。
這種對話方式可以是:
- 交互的方式:從鍵盤輸入命令,通過 /bin/bash 的解析,可以立即得到 Shell 的回應
[root@mico ~]# echo "hello"
hello
[root@mico ~]# pwd
/root
[root@mico ~]#
- 非交互的方式: 腳本
什么是Shell腳本
- Linux命令、變量和流程控制語句等有機的結合起來并以程序文件形式執行,該程序稱為shell腳本。
- Shell腳本語言是弱類型語言(無須定義變量的類型即可使用), shell腳本擅長處理純文本類型的數據,而linux中,幾乎所有的配置文件,日志,都是純文本類型文件。
腳本語言的種類
編譯型語言
定義:指用專用的編譯器,針對特定的操作平臺(操作系統)將某種高級語言源代碼一次性翻譯成可被硬件平臺直接運行的二進制機器碼(具有操作數,指令、及相應的格式),這個過程叫做編譯(./configure make makeinstall );編譯好的可執行性文件(.exe),可在相對應的平臺上運行(移植性差,但運行效率高)。典型的編譯型語言有, C語言、C++等。另外,Java語言是一門很特殊的語言,Java程序需要進行編譯步驟,但并不會生成特定平臺的二進制機器碼,它編譯后生成的是一種與平臺無關的字節碼文件(*.class)(移植性好的原因),這種字節碼自然不能被平臺直接執行,運行時需要由解釋器解釋成相應平臺的二進制機器碼文件;大多數人認為Java是一種編譯型語言,但我們說Java即是編譯型語言,也是解釋型語言也并沒有錯。
解釋型語言
定義:指用專門解釋器對源程序逐行解釋成特定平臺的機器碼并立即執行的語言;相當于把編譯型語言的編譯鏈接過程混到一起同時完成的。解釋型語言執行效率較低,且不能脫離解釋器運行,但它的跨平臺型比較容易,只需提供特定解釋器即可。常見的解釋型語言有, Python(同時是腳本語言)與Ruby等。
腳本語言
定義:為了縮短傳統的編寫-編譯-鏈接-運行(edit-compile-link-run)過程而創建的計算機編程語言。特點:程序代碼即是最終的執行文件,只是這個過程需要解釋器的參與,所以說腳本語言與解釋型語言有很大的聯系。腳本語言通常是被解釋執行的,而且程序是文本文件。
典型的腳本語言有,JavaScript,Python,shell等。
其他常用的腳本語句種類
- PHP是網頁程序,也是腳本語言。是一款更專注于 web 頁面開發(前端展示)的腳本語言,例如:Dedecms,discuz。PHP 程序也可以處理系統日志,配置文件等,php 也可以調用系統命令。
- Perl 腳本語言。比 shell 腳本強大很多,語法靈活、復雜,實現方式很多,不易讀,團隊協作困難,但仍不失為很好的腳本語言,存世大量的程序軟件。MHA 高可用 Perl 寫的
- Python,不但可以做腳本程序開發,也可以實現 web 程序以及軟件的開發。近兩年越來越多的公司都會要求會 Python。
Shell腳本與 php/perl/python 語言的區別和優勢?
shell 腳本的優勢在于處理操作系統底層的業務 (linux 系統內部的應用都是 shell 腳本完成)因為有大量的 linux 系統命令為它做支撐。2000 多個命令都是 shell 腳本編程的有力支撐,特別是grep、awk、sed 等。例如:一鍵軟件安裝、優化、監控報警腳本,常規的業務應用,shell 開發更簡單快速,符合運維的簡單、易用、高效原則。PHP、Python 優勢在于開發運維工具以及 web 界面的管理工具,web 業務的開發等。處理一鍵軟件安裝、優化,報警腳本。常規業務的應用等 php/python 也是能夠做到的。但是開發效率和復雜比用 shell 就差很多了。
系統中的shell
查看系統環境
[root@mico ~]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[root@mico ~]# uname -r
3.10.0-862.11.6.el7.x86_64
[root@mico ~]# getenforce
Disabled
[root@mico ~]# systemctl status firewalld.service
● firewalld.service - firewalld - dynamic firewall daemonLoaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)Active: inactive (dead)Docs: man:firewalld(1)
[root@mico ~]#
查看系統中的命解釋器
[root@mico ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
[root@mico ~]#
常用操作系統的默認shell
- Linux是Bourne Again shell(bash)
- Solaris和FreeBSD缺省的是Bourne shell(sh)
- AIX下是Korn Shell(ksh)
- HP-UX缺省的是POSIX shell(sh)
[root@mico ~]# echo $SHELL
/bin/bash
查看系統的 Bash 版本
[root@VM_42_34_centos /]# head -1 /etc/init.d/network
#! /bin/bash
[root@VM_42_34_centos /]# head -1 /etc/init.d/netconsole
#!/bin/bash
bash版本
[root@mico ~]# bash -version
GNU bash, 版本 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
許可證 GPLv3+: GNU GPL 許可證版本3或者更高 <http://gnu.org/licenses/gpl.html>這是自由軟件,您可以自由地更改和重新發布。
在法律允許的范圍內沒有擔保.
bash 破殼漏洞
使用 命令 env x='() { :;}; echo be careful' bash -c "echo this is a test"
如果返回結果為一行,則為正常,
[root@mico ~]# env x='() { :;}; echo be careful' bash -c "echo this is a test"
this is a test
#解決辦法 升級當前的bash版本yum install update bash
bash 和 sh 的區別
早期的bash和sh稍有不同,還包含了csh和ksh的特色,但大多數腳本都可以不加修改地在sh上運行
[root@VM_42_34_centos ~]# ll /bin/bash
-rwxr-xr-x 1 root root 964544 4月 11 2018 /bin/bash
[root@VM_42_34_centos ~]# ll /bin/sh
lrwxrwxrwx 1 root root 4 8月 10 2018 /bin/sh -> bash
說明:sh為bash的軟鏈接,一般情況下,腳本開頭使用“#!/bin/bash”和"#!/bin/sh"是沒有區別的,但更規范的寫法是在腳本開頭使用“#!/bin/bash”
下面的Shell腳本是系統自帶的軟件啟動腳本的開頭部分
sh與bash 的關系
[root@mico ~]# ll /bin/sh
lrwxrwxrwx 1 root root 4 8月 10 2018 /bin/sh -> bash
/bin與 /user/bin 的關系
[root@mico ~]# ll /bin -d
lrwxrwxrwx 1 root root 7 8月 10 2018 /bin -> usr/bin
腳本書寫規范
腳本統一存放目錄
[root@mico ~]# mkdir -p /server/scripts/
[root@mico ~]# cd /server/scripts/
選擇解釋器
腳本第一行指出由哪個程序(解釋器)來執行腳本中的內容#!/bin/bash
或#!/bin/sh
(255個字符以內)
注意格式 ↓
其中開頭的"#!"字符又稱為幻數,在執行bash腳本的時候,內核會根據"#!"后的解釋器來確定該用那個程序解釋這個腳本中的內容。
[root@mico scripts]# head -1 /etc/init.d/*
==> /etc/init.d/functions <==
# -*-Shell-script-*-==> /etc/init.d/netconsole <==
#!/bin/bash==> /etc/init.d/network <==
#! /bin/bash==> /etc/init.d/README <==
You are looking for the traditional init scripts in /etc/rc.d/init.d,
編輯腳本使用vim
使用 .vimrc
文件,將下面的信息添加到 .vimrc
文件中,能夠快速的生成開頭的注釋信息
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"func SetTitle()if expand("%:e") == 'sh'call setline(1,"#!/bin/bash")call setline(2, "##############################################################")call setline(3, "# File Name: ".expand("%"))call setline(4, "# Version: V1.0")call setline(5, "# Author: mico")call setline(6, "# Created Time : ".strftime("%F %T"))call setline(7, "# Description:")call setline(8, "##############################################################")call setline(9, "")endif
endfunc
使用后的效果
#!/bin/bash
##############################################################
# File Name: scripts_tmp.sh
# Version: V1.0
# Author: mico
# Created Time : 2019-07-09 10:12:27
# Description:
##############################################################
在Shell腳本中,跟在#
后面的內容表示注釋。注釋部分不會被執行,僅給人看。注釋可以自成一行,也可以跟在命令后面,與命令同行。要養成寫注釋的習慣,方便自己與他人。
最好不用中文注釋,因為在不同字符集的系統會出現亂碼。(字符集為zh_CN.UTF-8,為中文)。
文件名規范
名字要有意義,并且結尾以 .sh
結束(并不是必須)
開發的規范和習慣小結
- 放在統一的目錄
- 腳本以
.sh
為擴展名 - 開頭指定腳本解釋器。
- 開頭加版本版權等信息,可配置
~/.vimrc
文件自動添加。 - 腳本不要用中文注釋,盡量用英文注釋。
- 代碼書寫優秀習慣
- 成對的內容一次性寫出來,防止遺漏,如
[ ]
、' '
、" "
等 [ ]
兩端要有空格,先輸入[ ]
,退格,輸入 2 個空格,再退格寫。- 流程控制語句一次書寫完,再添加內容。(
if 條件 ; then 內容;fi
) - 通過縮進讓代碼易讀。
- 腳本中的引號都是英文狀態下的引號,其他字符也是英文狀態。
- 成對的內容一次性寫出來,防止遺漏,如
shell腳本的執行
當 Shell 腳本運行時,會先查找系統環境變量 ENV,該變量指定了環境文件(加載順序通常是 /etc/profile
、~/.bash_profile
、~/.bashrc
、/etc/bashrc
),在加載了上述變量文件后,Shell就開始執行腳本中的內容。
Shell 腳本是從上至下、從左至右依次執行每一行的命令及語句的。即執行完了一個命令后再執行下一個,如果在 Shell 腳本中(即腳本嵌套)
時,就會先執行子腳本的內容,完成后再返回父腳本繼續執行父腳本內后序的命令及語句。
通常情況下,在執行 Shell 腳本時,會向系統內核請求啟動一個新進程,以便在該進程或只能怪執行腳本的命令以及子Shell 腳本。
執行腳本的辦法
執行腳本的四種方法
(1)sh/bash scripts.sh
當腳本本身沒有可執行權限時使用的方法,或腳本文件開頭沒有指定解釋器時需要使用分方法。
(2)首先chown +x
,然后./scripts.sh
或.path/scripts.sh
在當前路徑下執行腳本(腳本需要有執行權限),通過絕對路徑或相對路徑直接執行腳本
(3)source scripts.sh
或 . (空格) scripts.sh
這種方法通常是使用 source
或 .
讀入或加載指定的 shell 腳本文件(不需要執行權限),然后依次執行指定 shell 腳本中的所有語句。這些語句將在當前 shell 腳本的進程中運行(其他幾種模式都會啟動新的進程執行子腳本)。source
或 .
命令的功能是在當前 Shell 中執行 source
或 .
加載并執行的相關腳本文件中的命令及語句,而不是產生一個子 Shell 來執行文件中的命令。
說明:.
或 source
命令的功能相同,都是讀入腳本并執行腳本中的命令。
(4)sh < script-name 或 cat scripts-name|sh 效率低
同樣適用于bash,不過這種用法不常見。
source 與 . (點) 的作用
soucre命令
[root@mico ~]# help source |head -2
source: source 文件名 [參數]在當前 shell 中執行一個文件中的命令。
. (點)
[root@mico ~]# help . |head -2
.: . 文件名 [參數]在當前 shell 中執行一個文件中的命令。
sh于source的區別
對比:用 source 執行腳本文件,執行過程不另開進程,腳本文件中設定的變量在當前shell中可以看到;
用 sh/bash 執行腳本文件,是在當前進程另開子進程來執行腳本命令,腳本文件中設定的變量在當前shell中不能看到。
例如
[root@VM_42_34_centos trsky]# echo 'userdir=`pwd`' >> testsource.sh
[root@VM_42_34_centos trsky]# cat testsource.sh
userdir=`pwd`
[root@VM_42_34_centos trsky]# sh testsource.sh
[root@VM_42_34_centos trsky]# echo $userdir[root@VM_42_34_centos trsky]# source testsource.sh
[root@VM_42_34_centos trsky]# echo $userdir
/home/trsky
[root@VM_42_34_centos trsky]#
由案例可知,使用 source
或 .
執行的腳本中的變量值會傳遞到當前的 Shell 下,因為使用 source
或 .
執行腳本不會開啟新的進程,而是在統一進程下完成腳本中所有指令的執行。
[root@mico scripts]# sh mico_test.sh
Hello World!
[root@mico ~]# echo $Mico
Mico # sh 新建一個Shell窗口(新建一個進程)執行一個文件中的命令。[root@mico scripts]# source mico_test.sh
Hello World!
[root@mico scripts]# echo $mico
Hello World!
面試題:
問 sh test.sh 后 echo $user 返回的結果_空 ?
[root@oldboy scripts]# cat test.sh
#!/bin/bash
user=`whoami`
Shell的變量
Shell變量的特性
Shell為弱類型語言,所以在默認情況下,在Bash Shell中是不會區分變量類型的。但Shell可以通 declare 顯示定義變量的類型。
什么是變量
變量可以分為兩類:環境變量(全局變量)和普通變量(局部變量)
- 環境變量也可稱為全局變量,可以在創建他們的Shell及其派生出來的任意子進程shell中使用,環境變量又可分為自定義環境變量和Bash內置的環境變量
- 普通變量也可稱為局部變量,只能在創建他們的Shell函數或Shell腳本中使用。普通變量一般是由開發者用戶開發腳本程序時創建的。
- 特殊變量
Shell中定義變量名以及為變量內容賦值要求
變量名一般由字母、數字、下劃線組成,可以由字母或下劃線開頭
把一個命令的結果作為變量的內容賦值的方法
- 變量名=
ls
- 變量名=$(ls)
環境變量
使用 env/declare/set/export -p
命令查看系統中的環境變量,這三個命令的的輸出方式稍有不同。
[root@mico scripts]# env
XDG_SESSION_ID=1
HOSTNAME=mico
TERM=linux
SHELL=/bin/bash
HISTSIZE=1000
SSH_CLIENT=10.0.0.1 5537 22
SSH_TTY=/dev/pts/0
USER=root
~~~
輸出一個系統中的環境變量
[root@mico ~]# echo $LANG
zh_CN.UTF-8
自定義環境變量
設置環境變量export
和 declare
設置環境變量的3種方法
- export 變量名=value
- 變量名=value; export 變量名
- declare -x 變量名=value
自定義全局變量的實例:
[root@VM_42_34_centos ~]# cat /etc/profile|grep Mico
export Mico='mico'
[root@VM_42_34_centos ~]# source /etc/profile
[root@VM_42_34_centos ~]# echo $MIco[root@VM_42_34_centos ~]# echo $Mico
mico
[root@VM_42_34_centos ~]# env|grep Mico
Mico=mico
[root@VM_42_34_centos ~]#
讓環境變量永久生效的常用設置文件
- 用戶環境變量配置
# 用戶的環境變量設置,比較常見的是用戶家目錄下的.bashrc 和 .bash_profile
[root@mico ~]# ls /root/.bashrc
/root/.bashrc
[root@mico ~]# ls /root/.bash_profile
/root/.bash_profile
[root@mico ~]#
- 全局環境變量的配置
常見的全局環境變量的配置文件如下:
/etc/profile
/etc/bashrc
/etc/profile.d/
如果要在登陸后初始化或顯示加載內容,則將腳本文件放在/etc/profile.d/下即可(無需執行權限)
設置登錄提示的兩種方式
- 在
/etc/motd
里增加提示的字符串
[root@mico ~]# vim /etc/motd
[root@mico ~]# cat /etc/motd
Welcome to Linux.
- 在
/etc/profile.d/
目錄下添加腳本
[root@mico profile.d]# cat /etc/profile.d/mico.sh
#!/bin/bash
##############################################################
# File Name: /etc/profile.d/mico.sh
# Version: V1.0
# Author: mico
# Created Time : 2019-07-01 10:00:06
# Description:
##############################################################
echo "Hello, you are studing Linux"
登錄后出現
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.Welcome to Linux.
Hello, you are studing Linux
[root@mico ~]#
顯示與取消環境變量
- 通過 echo 或 prinf 打印環境變量
#常見的系統環境變量
$HOME:用戶登錄時進入的目錄
$UID:當前用戶的UID(用戶標識),相當于 id-u
$PWD:當前工作目錄的絕對路徑名
$SHELL:當前Shell
$USER:當前yonghu
...
打印或刪除環境變量
#打印環境
printf "$Home\n"
echo $PWD
- 使用unset取消環境變量
#刪除環境變量
unset PWD
普通變量
本地變量在用戶當前的Shell生存期的腳本中使用。例如,本地變量OLDBOY取值為bingbing,這個值在用戶當前Shell生存期中有意義。如果在Shell中啟動另一個進程或退出,本地變量值將無效
普通變量的定義
變量名=value //解釋變量名
變量名='value' //不解釋變量名
變量名="value" //解釋變量名
定義普通變量實踐
[root@mico ~]# a=1
[root@mico ~]# b='2'
[root@mico ~]# c="3"
[root@mico ~]# echo "$a"
1
[root@mico ~]# echo "$b"
2
[root@mico ~]# echo "${c}"
提示:$變量名表示輸出變量,可以用$c和${c}兩種用法
【小結】連續普通字符串內容賦值給變量,不管用什么引號或者不用引號,它的內容是什么,打印變量就輸出什么
export命令
[root@mico ~]# help export
export: export [-fn] [名稱[=值] ...] 或 export -p
為 shell 變量設定導出屬性。
標記每個 NAME 名稱為自動導出到后續命令執行的環境。如果提供了 VALUE
則導出前將 VALUE 作為賦值。
export命令的說明
- 當前shell窗口及子shell窗口生效
- 在新開的shell窗口不會生效,生效需要寫入配置文件
定義變量
[root@mico scripts]# CSLN=mico
[root@mico scripts]# export CSLN1=1
# 當前窗口查看
[root@mico scripts]# echo $CSLN
mico
[root@mico scripts]# echo $CSLN1
1
# 編寫測試腳本
[root@mico scripts]# vim quanju.sh
#!/bin/bash
echo $CSLN
echo $CSLN1
# 使用sh執行
[root@mico scripts]# sh quanju.sh 1
# 使用source 執行
[root@mico scripts]# source quanju.sh
mico
1
環境變量相關配置文件
- /etc/proflie
- /etc/bashrc
- ~/.bashrc
- ~/.bash_profile
- /etc/proflie.d/ # 目錄
四文件讀取順序(CentOS6和7都一樣)
① /etc/profile
② ~/.bash_profile
③ ~/.bashrc
④ /etc/bashrc
系統運行Shell的三種方式
1) 通過系統用戶登陸后默認運行的shell
2) 非登錄交互式運行Shell
3) 執行腳本運行非交互式Shell
文件讀取過程示意圖
驗證四文件讀取順序的方法
sed -i '1a echo "$(date +%T-%s) /etc/profile1" >>/tmp/mico' /etc/profile
sed -i '$a echo "$(date +%T-%s) /etc/profile2" >>/tmp/mico' /etc/profile
sed -i '1a echo "$(date +%T-%s) /etc/bashrc1" >>/tmp/mico' /etc/bashrc
sed -i '$a echo "$(date +%T-%s) /etc/bashrc2" >>/tmp/mico' /etc/bashrc
sed -i '1a echo "$(date +%T-%s) ~/.bashrc1" >>/tmp/mico' ~/.bashrc
sed -i '$a echo "$(date +%T-%s) ~/.bashrc2" >>/tmp/mico' ~/.bashrc
sed -i '1a echo "$(date +%T-%s) ~/.bash_profile1" >>/tmp/mico' ~/.bash_profile
sed -i '$a echo "$(date +%T-%s) ~/.bash_profile2" >>/tmp/mico' ~/.bash_profile
環境變量的知識小結
- 變量名通常要大寫。
- 變量可以在自身的Shell及子Shell中使用。
- 常用export來定義環境變量。
- 執行env默認可以顯示所有的環境變量名稱及對應的值。
- 輸出時用“$變量名”,取消時用“unset變量名”。
- 書寫crond定時任務時要注意,腳本要用到的環境變量最好先在所執行的Shell腳本中重新定義。
- 如果希望環境變量永久生效,則可以將其放在用戶環境變量文件或全局環境變量文件里。
環境變量
環境變量一般是指用export內置命令導出的變量,用戶定義shell的運行環境。保證shell命令的正確執行。Shell通過環境變量來確定登錄用戶名、命令路徑、終端類型、登錄目錄等。如果需要永久保存環境變量,可在用戶家目錄下的.bash_profile或.bashrc(非用戶登錄模式特有,例如遠程登錄SSH)文件中,或者全局配置/etc/bashrc(非用戶登錄模式特有,如遠程SSH)或/etc/profile文件中定義。在將環境變量放入上述的文件之后,每次用戶登錄時這些變量將被初始化。
查看設置的變量:set、env、declare
- set:輸出所有的變量,包括全局變量和局部變量。set -o 顯示bash Shell的所有參數配置信息
- env:只顯示全局變量
- declare:輸出所有的變量、函數、整數和已經導出的變量
變量中引號的使用
- 只有在變量的值中有空格的時候,會使用引號。
- 單引號與雙引號的區別在于,是否能夠解析特殊符號。
[root@mico ~]# name=vicodona
[root@mico ~]# name2='mico'
[root@mico ~]# name3="you are my heart"
[root@mico ~]# echo $name
vicodona
[root@mico ~]# echo $name2
mico
[root@mico ~]# echo $name3
you are my heart
[root@mico ~]# name4='mi co'
[root@mico ~]# echo $name4
mi co
[root@mico ~]# name5="mi co"
[root@mico ~]# echo $name5
mi co
[root@mico ~]# name6="mico $PWD"
[root@mico ~]# echo $name6
mico /root
[root@mico ~]# name6='mi co $PWD'
[root@mico ~]# echo $name6
mi co $PWD
[root@mico ~]# name7="mico $PWD"
[root@mico ~]# echo $name7
mico /root
普通變量的要求
- 內容是純數字、簡單的連續字符(內容中不帶任何空格)時,定義時可以不加任何引號,例如:
a.MicoAge=22
b.NETWORKING=yes
- 沒有特殊情況時,字符串一律用雙引號定義賦值,特別是多個字符串中間有空格時,例如:
a.NFSD_MODULE="no load"
b.MyName="Oldboy is a handsome boy."
- 當變量里的內容需要原樣輸出時,要用單引號(M),這樣的需求極少,例如:
a.OLDBOY_NAME='OLDBOY'
變量使用反引號賦值
[root@mico scripts]# time=`date`
[root@mico scripts]# echo $time
2017年 12月 05日 星期二 09:02:06 CST[root@mico scripts]# file=`ls`
[root@mico scripts]# echo $file
mico_test.sh panduan.sh quanju.sh yhk.sh
使用${}
打印變量的時候防止出現“金庸新著”的問題
[root@mico ~]# time=`date`
[root@mico ~]# echo $time
2019年 07月 18日 星期四 17:47:23 CST
[root@mico ~]# echo ${time}_day
2019年 07月 18日 星期四 17:47:23 CST_day
[root@mico ~]# echo $time-day
2019年 07月 18日 星期四 17:47:23 CST-day
編寫腳本測試${}
# 使用腳本測試[root@mico ~]# vim variable.sh #!/bin/bashtime=`date`echo $timedayecho ${time}day[root@mico ~]# sh variable.sh 2019年 07月 18日 星期四 17:49:51 CSTday
名稱 | 解釋 |
---|---|
單引號 | 所見即所得,即輸出時會將單引號內的所有內容都原樣輸出,或者描述為單引號里面看到的是什么就會輸出什么;這稱為強引用 |
雙引號(默認) | 輸出雙引號內的所有內容;如果內容中有命令、變量、特殊字符等,會先把變量、命令、轉義字符解析出結果,然后再輸出最終內容,推薦使用,稱為弱引用 |
無引號 | 賦值時,如果變量內容中有空格,則會造成賦值不完整。而在輸出內容時,會將含有空格的字符串視為一個整體來輸出;如果內容中有命令、變量等,則會先把變量、命令解析出結果,然后輸出最終內容;如果字符串中帶有空格等特殊字符,則有可能無法完整的輸出,因此需要改加雙引號 |
反引號 | 一般用于引用命令,執行的時候命令會被執行,相當于`$()`,賦值和輸出都要用 命令引起來 |
定義變量名技巧
- 變量名只能為字母、數字或下劃線,只能以字母或下劃線開頭。
- 變量名的定義要有一定的規范,并且要見名知意。
示例:
MicoAge=22 #<==每個單詞的首字母大寫的寫法
mico_age=22 #<==單詞之間用"_"的寫法
micoAgeSex=man #<==駝峰語法:首個單詞的首字母小寫,其余單詞首字母大寫
MICOAGE=22 #<==單詞全大寫的寫法
- 一般的變量定義、賦值常用雙引號;簡單連續的字符串可以不加引號;希望原樣輸出時使用單引號。
- 希望變量的內容是命令的解析結果時,要用反引號'',或者用$()把命令括起來再賦值。
特殊變量
位置變量
常用的特殊位置參數說明
位置變量 | 作用說明 |
---|---|
$0 | 獲取當前執行的shell腳本的文件名,如果執行腳本帶路徑那么就包括腳本路徑。 |
$n | 獲取當前執行的shell腳本的第n個參數值,n=1..9,當n為0時表示腳本的文件名,如果n大于9用大括號括起來{10},參數以空格隔開。 |
$# | 獲取當前執行的shell腳本后面接的參數的總個數 |
$* | 獲取當前shell的所有傳參的參數,不加引號同$@;如果給$加上雙引號,例如: “$”,則表示將所有的參數視為單個字符串,相當于“112$3”。 |
$@ | 獲取當前shell的所有傳參的參數,不加引號同$*;如果給$@加上雙引號,例如: “$@”,則表示將所有參數視為不同的獨立字符串,相當于“$1” “$2” “$3” “……”,這是將參數傳遞給其他程序的最佳方式,因為他會保留所有內嵌在每個參數里的任何空白。 |
當$*
和$@
都加雙引號時,兩者有區別,都不加雙引號時,兩者無區別。
舉例說:
腳本名稱叫 test.sh 入參三個: 1 2 3
運行 test.sh 1 2 3后
$* 為 "1 2 3"(一起被引號包住)
$@ 為 "1" "2" "3"(分別被包住)
參數實踐
[root@mico ~]# vim parameter.sh
#!/bin/bashecho $0
echo "第一個參數:" $1
echo "第二個參數:" $2
echo "第11個參數:" ${11}[root@mico ~]# sh parameter.sh
parameter.sh
第一個參數:
第二個參數:
第11個參數:
[root@mico ~]#
[root@mico ~]# sh parameter.sh 1 2 3 4 5 6 7 8 9 10 11
parameter.sh
第一個參數: 1
第二個參數: 2
第11個參數: 11
[root@mico ~]#
$# 參數實踐
[root@mico ~]# cat parameter.sh
#!/bin/bash
##############################################################
# File Name: parameter.sh
# Version: V1.0
# Author: mico
# Created Time : 2019-07-18 17:50:45
# Description:
##############################################################
echo $0
echo "第一個參數:" $1
echo "第二個參數:" $2
echo "第10個參數:" ${10}
echo "第11個參數:" ${11}
echo "參數個數:" $#[root@mico ~]# sh parameter.sh 1 2 3 4 5 6 7 8 9 10 11
parameter.sh
第一個參數: 1
第二個參數: 2
第10個參數: 10
第11個參數: 11
參數個數: 11
$* 參數實踐
[root@mico ~]# vim parameter.sh#!/bin/bash
##############################################################
# File Name: parameter.sh
# Version: V1.0
# Author: mico
# Created Time : 2019-07-18 17:50:45
# Description:
##############################################################
echo $0
echo "第一個參數:" $1
echo "第二個參數:" $2
echo "第10個參數:" ${10}
echo "第11個參數:" ${11}
echo "參數個數:" $#
echo "參數:"$*
[root@mico ~]# sh parameter.sh 1 2 3 4 5 6 7 8 9 10 11
parameter.sh
第一個參數: 1
第二個參數: 2
第10個參數: 10
第11個參數: 11
參數個數: 11
參數:1 2 3 4 5 6 7 8 9 10 11
$*
與$@
對比實踐
[root@mico scripts]# set -- "I am" handsome boy..
[root@mico scripts]# echo $1
I am
[root@mico scripts]# echo $2
handsome
[root@mico scripts]# echo $3
boy..
[root@mico scripts]# echo $*
I am handsome boy..
[root@mico scripts]# echo $@
I am handsome boy..[root@mico scripts]# for i in $*;do echo $i ;done
I
am
handsome
boy..
[root@mico scripts]# for i in $@;do echo $i ;done
I
am
handsome
boy..
[root@mico scripts]# for i in "$@";do echo $i ;done
I am
handsome
boy..
[root@mico scripts]# for i in "$*";do echo $i ;done
I am handsome boy..
進程狀態變量
Shell進程的特殊狀態變量說明
位置變量 | 作用說明 |
---|---|
$? | 獲取執行上一個指令的執行狀態返回值(0為成功,非零為失敗),這個變量最常用 |
$$ | 獲取當前執行的Shell腳本的進程號(PID),這個變量不常用,了解即可 |
$! | 獲取上一個在后臺工作的進程的進程號(PID),這個變量不常用,了解即可 |
$_ | 獲取在此之前執行的命令或腳本的最后一個參數,這個變量不常用,了解即可 |
進程參數實踐
[root@mico scripts]# echo $?
0
[root@mico scripts]# echo $$
1368
[root@mico scripts]# echo $![root@mico scripts]# echo $_
Echo
Bash Shell常見的內部命令:echo
、eval
、exec
、export
、read
、shift
echo參數說明
參數 | 參數說明 |
---|---|
-n | 不要追加換行 |
-e | 啟用下列反斜杠轉義的解釋 |
-E | 顯式地抑制對于反斜杠轉義的解釋 |
echo' 對下列反斜杠字符進行轉義: +
\n:換行 +
\r:回車 +
\t:橫向制表符 +
\b:退格 +
\v:縱向制表符 +
\c`:抑制更多的輸出
定義變量的方式
三種定義變量的方式
- 直接賦值
- 傳參 (傳遞參數)
- 交互式設置變量,使用read命
read命令說明
在命令行中使用
[root@mico scripts]# read
132
[root@mico scripts]# echo $REPLY
132
[root@mico scripts]# read mico
456
[root@mico scripts]# echo $mico
456
[root@mico scripts]# echo $REPLY
132
在腳本中使用
[root@mico scripts]# vim mico_test.sh
#!/bin/bash
read -p '請輸入:' micoecho $mico
執行結果
[root@mico scripts]# sh mico_test.sh
請輸入:mico_znix
mico_znix
read 命令的幫助說明
[root@mico scripts]# read --help
-bash: read: --: 無效選項
read: 用法:read [-ers] [-a 數組] [-d 分隔符] [-i 緩沖區文字] [-n 讀取字符數] [-N 讀取字符數] [-p 提示符] [-t 超時] [-u 文件描述符] [-s不顯示終端的任何輸入] [名稱 ...]
定義方法實踐
直接賦值方法
[root@mico ~]# cat parameter.sh
#!/bin/bash
##############################################################
# File Name: parameter.sh
# Version: V1.0
# Author: mico
# Created Time : 2019-07-18 17:50:45
# Description:
##############################################################
name=MICO
age=18
sex=girl
hobby=`ls`
ethFile=/etc/sysconfig/network-scripts/ifcfg-eth0echo $hobby
ls $ethFile
[root@mico ~]# sh parameter.sh
default.pass Django-Blog Django-Blog.tar parameter.sh practices scripts server test.sh variable.sh
/etc/sysconfig/network-scripts/ifcfg-eth0
交互式設置變量 read
[root@mico ~]# vim keyword.sh
#!/bin/bash
##############################################################
# File Name: keyword.sh
# Version: V1.0
# Author: mico
# Created Time : 2019-07-18 18:42:31
# Description:
##############################################################
read -p "請輸入您的學號:" number
read -s -p "請輸入姓名:" name
echo
echo "您的學號:"$number
echo "您的姓名:"$name
[root@mico ~]# sh keyword.sh
請輸入您的學號:20190729
請輸入姓名:
您的學號:20190729
您的姓名:youka
寫一個交互腳本,實現能夠定義主機名及IP地址
腳本內容↓
[root@mico ~]# cat keyword.sh
#!/bin/bash
##############################################################
# File Name: keyword.sh
# Version: V1.0
# Author: mico
# Created Time : 2019-07-18 18:42:31
# Description:
##############################################################
read -p "請輸入您的學號:" number
read -s -p "請輸入姓名:" name
echo
echo "您的學號:"$number
echo "您的姓名:"$name[root@mico ~]# vim host_start.sh
[root@mico ~]# sh host_start.sh
請輸入主機名:mico
請輸入IP地址的主機位:180
是否重啟服務器:{yes/no}no
請稍后手動重啟系統!
變量的子串
變量子串說明
表達式 | 說明 |
---|---|
${parameter} | 返回變量$parameter的內容 |
${#parameter} | 返回變內容的長度(按字符),也適用于特殊變量 |
${parameter:offset} | 在變量${parameter}中,從位置offset之后開始提取子串到結尾 |
${parameter:offset:length} | 在變量${parameter}中,從位置offset之后開始提取長度為length的子串 |
${parameter#word} | 從變量${parameter}開頭開始刪除最短匹配的word子串 |
${parameter##word} | 從變量${parameter}開頭開始刪除最長匹配的word子串 |
${parameter%word} | 從變量${parameter}結尾開始刪除最短匹配的word子串 |
${parameter%%word} | 從變量${parameter}結尾開始刪除最長匹配的word子串 |
${parameter/pattem/string} | 使用string代替第一個匹配的pattern |
${parameter//pattem/string} | 使用string代替所有匹配的pattern |
計算變賦值的長度
- echo $(#Mico) :這種方式最快
- echo $Mico|wc -L
- expr length "$Mico"
- echo "$Mico"|awk '{print length($0)}'
[root@mico ~]# export TEMP="Here is Linux"
[root@mico ~]# echo ${#TEMP}
13
[root@mico ~]# echo $TEMP|wc -L
13
[root@mico ~]# expr length "$TEMP"
13
[root@mico ~]# echo "$TEMP"
Here is Linux
[root@mico ~]# echo "$TEMP"|awk '{print length($0)}'
13
[root@mico ~]# time echo ${#TEMP}
13real 0m0.000s
user 0m0.000s
sys 0m0.000s
[root@mico ~]# time echo $TEMP|wc -L
13real 0m0.002s
user 0m0.000s
sys 0m0.002s
[root@mico ~]#
截取變量中的字符
[root@mico scripts]# mico=abcABC123ABCabc
[root@mico scripts]# echo ${mico#abc}
ABC123ABCabc
[root@mico scripts]# echo ${mico##abc}
ABC123ABCabc
[root@mico scripts]# echo ${mico%abc}
abcABC123ABC
[root@mico scripts]# echo ${mico%%abc}
abcABC123ABC
[root@mico scripts]# echo ${mico#a*c}
ABC123ABCabc
[root@mico scripts]# echo ${mico##a*c}[root@mico scripts]# echo ${mico%a*c}
abcABC123ABC
[root@mico scripts]# echo ${mico%%a*c}[root@mico scripts]# echo ${mico#a*C}
123ABCabc
[root@mico scripts]# echo ${mico#a*C}
123ABCabc
[root@mico scripts]# echo ${mico##a*C}
abc
[root@mico scripts]# echo ${mico%a*c}
abcABC123ABC
[root@mico scripts]# echo ${mico%A*c}
abcABC123
[root@mico scripts]# echo ${mico%%A*c}
abc
替換變量內容
[root@mico scripts]# echo $mico
abcABC123ABCabc
[root@mico scripts]# echo ${mico/abc/mico}
micoABC123ABCabc
[root@mico scripts]# echo ${mico//abc/mico}
micoABC123ABCmico
echo $(#parameter)
處理最快的原因是一般情況下調用外部命令來處理的方式與使用內置操作的速度相差較大。在Shell編程中應該盡量使用內置命令。
有關獲取字符串長度的幾種統計方法的性能比較
- 變量自帶的計算長度的方法效率最高,在要求效率的場景中盡量多用
- 使用管道統計的方法的效率都比較差,在要求效率的場景中盡量不用
- 對于日常簡單的腳本計算,可以根據自己所擅長的或易用的程度去選擇
有關上述匹配刪除的小結
+#
表示從幵頭刪除匹配最短。
##
表示從開頭刪除匹配最長。%
表示從結尾刪除匹配最短。%%
表示從結尾刪除匹配最長。a*c
表示匹配的突符串,*
表示匹配所有,a*c
匹配開頭為 a、中間為任意多個字符、結尾為c的字符串。a*C
表示匹配的字符串,*
表示匹配所有,a*C
匹配開頭為 a、中間為任意多個字符、結尾為C的字符串。
有關替換的小結
- 一個“/”表示替換匹配的第-個字符串。
- 兩個“/”表示替換匹配的所有字符串。
Shell的特殊擴展變量說明
表達式 | 說明 |
---|---|
${parameter:-word} | 如果parameter的變量值為空或未賦值,則會返回word字符串并替代變量的值用途.如果變量未定義,則返回備用的值,防止變量為空值或因未定義而導致異常 |
${parameter:=word} | 如果parameter的變量值為空或未賦值,則設置這個變量值為word,并返回其值。位置變量和特殊變量不適用用途:基本同上一個${parameter>word},但該變量又額外給parameter變量賦值了 |
${parameter:?word} | 如果parameter變量值為空或未賦值,那么word字符串將被作為標準錯誤輸出,否則輸出變量的值。用途:用于捕捉由于變量未定義而導致的錯誤,并退出程序 |
${parameter:+word} | 如果parameter變量值為空或未賦值,則什么都不做,否則word字符串將替代變量的值 |
變量的數值計算
Shell中常見的算術運算符號
運算符 | 意義 順序 | |
---|---|---|
++ - - | 增加及減少,可前置/可結尾 | 由左至右 |
+ | 一元正號 | 由右至左 |
- | 一元負號 | 由右手至左 |
! | 邏輯與 | 由右手至左 |
~ | 位的取反 | 由右手至左 |
* / % | 乘法、除法、取余數 | 由左至右 |
+ | 加法 | 由左至右 |
- | 減法 | 由左至右 |
<< >> | 向左位移、向右位移 | 由左至右 |
< <= > >= | 比較運算符 | 由左至右 |
== != | 相等 不相等 | 由左至右 |
& | 位的AND | 由左至右 |
^ | 位的Exclusive OR | 由左至右 |
| | 位的OR | 由左至右 |
&& | 邏輯AND | 由左至右 |
|| | 邏輯OR | 由左至右 |
?: | 條件表達式 | 由右至左 |
= += -= *= /= %= &= ^= | 賦值運算符 |
Shell中常見的算術運算命令
運算操作符與運算命令 | 意義 |
---|---|
(()) | 用于整數運算的常用運算符,效率高 |
let | 用于整數運算,類似于"(())" |
expr | 可用于整數運算,但還有很多其他的額外功能 |
bc | Linux下的一個計算器程序(適合整數以及小數運算) |
$[] | 用于整數的運算 |
awk | awk既可以用于整數運算,也可以用于小數運算 |
declare | 定義變量值和屬性,-i參數可以用于定義整形變量,做運算 |
雙小括號(())
的操作方法
運算操作符與運算命令 | 意義 |
---|---|
((i=i+1)) | 此種書寫方法為運算后賦值法,即將i+1的運算結構賦值給變量i。注意,不能用"echo((i=i+1))"的形式輸出表達式的值,但可以用echo$((i=i+1))輸出其值 |
i=$((i+1)) | 可以在"(())"前加$符,表示將表達式運算后復制給i |
((8>7&&5==5)) | 可以進行比較操作,還可以加入邏輯與和邏輯或,用于條件判斷 |
echo $((2+1)) | 需要直接輸出運算表達式的運算結果時,可以在"(())"前加$符 |
【提示】上面涉及的數字及變量必須為整數(整型),不能是小數(浮點數)或字符串。后面的bc
和awk
命令可以用于進行小數(浮點數)運算,但一般用到的較少。
僅支持整數的運算
echo $((數學運算表達式))
# 形式一
[root@mico scripts]# echo $((1 + 1))
2
[root@mico scripts]# echo $((2*7-3/6+5))
19
# 形式二
[root@mico scripts]# ((mico=2*8))
[root@mico scripts]# echo $mico
16
# 形式三
[root@mico scripts]# znix=$((2*7-3/6+5))
[root@mico scripts]# echo $znix
19
延伸產物(重要)
- i++ 自增1
- i-- 自減1
- ++i
- --i
示例:
[root@mico scripts]# i=1
[root@mico scripts]# echo $((i++))
1
[root@mico scripts]# echo $((i++))
2
[root@mico scripts]# echo $((i--))
3
[root@mico scripts]# echo $((i--))
2
[root@mico scripts]# echo $((i--))
1
[root@mico scripts]# echo $((++i))
1
[root@mico scripts]# echo $((++i))
2
[root@mico scripts]# echo $((++i))
3
[root@mico scripts]# echo $((--i))
2
[root@mico scripts]# echo $((--i))
1
[root@mico scripts]# echo $((--i))
0
記憶方法:++,--
變量a在前,表達式的值為a,然后a自增或自減,變量a在符號后,表達式值自增或自減,然后a值自增或自減。
let命令
[root@mico scripts]# i=1
[root@mico scripts]# i=i+1
[root@mico scripts]# echo $i
i+1[root@mico scripts]# i=1
[root@mico scripts]# let i=i+1
[root@mico scripts]# echo $i
2
expr 命令
- 整數計算
- 判斷擴展名
- 判斷輸入是否為整數,非整數返回值為2
- 計算變量的長度
示例
[root@mico scripts]# expr 1+1
1+1
[root@mico scripts]# expr 1 + 1
2
[root@mico scripts]# expr 1 * 1
expr: 語法錯誤
[root@mico scripts]# expr 1 \* 1
1
非整數返回值為2 示例:
[root@mico scripts]# expr 1 + 1
2
[root@mico scripts]# echo $?
0
[root@mico scripts]# expr -1 + 1
0
[root@mico scripts]# echo $?
1
[root@mico scripts]# expr a + 1
expr: 非整數參數
[root@mico scripts]# echo $?
2
$[]運算符
[root@mico scripts]# echo $[1+2]
3
[root@mico scripts]# echo $[1-2]
-1
[root@mico scripts]# echo $[1*2]
2
[root@mico scripts]# echo $[1/2]
0
typeset 命令進行運算
[root@mico scripts]# typeset -i A=2017 B=2018
[root@mico scripts]# A=A+B
[root@mico scripts]# echo $A
4035
可以進行小數運算的命令
bc 命令
# 安裝 bc 依賴于base源
[root@mico scripts]# yum -y install bc交互模式測試bc命令
[root@mico scripts]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
1+1
2
[root@mico scripts]# echo 1+1.1|bc
2.1
免交互模式測試bc命令
[root@mico scripts]# echo 'scale=6;1/3'|bc
.333333
python 命令
[root@mico scripts]# file `which yum `
/usr/bin/yum: Python script, ASCII text executable
[root@mico scripts]# python
>>> import os
>>> os.system('df -h')
>>> 1+1.1
2.1
>>>exit()
awk 命令
[root@mico ~]# echo "7.7 3.8"|awk '{print ($1-$2)}'
3.9
[root@mico ~]# echo "358 113"|awk '{print ($1-3)/$2}'
3.14159
[root@mico ~]# echo "3 9"|awk '{print ($1+3)*$2}'
54
[root@backup scripts]# awk BEGIN'{print 1.2+3.3}'
4.5
運算相關練習題
【練習題】實現一個加減乘除等功能的計算器
實現腳本:
[root@mico scripts]# cat calculator.sh
#!/bin/bashread -p "請輸入第一個整數:" a
read -p "請輸入第二個整數:" becho $a + $b =$(($a+$b))
echo $a - $b =$(($a-$b))
echo $a \* $b =$(($a*$b))
echo $a / $b =$(($a/$b))腳本執行過程:
[root@mico scripts]# sh calculator.sh
請輸入第一個整數:12
請輸入第二個整數:12
12 + 12 =24
12 - 12 =0
12 * 12 =144
12 / 12 =1
精簡方法
[root@mico scripts]# vim calculator2.sh
#!/bin/bashecho $(($1))腳本執行過程:
[root@mico scripts]# sh calculator2.sh 1+1
2
[root@mico scripts]# sh calculator.sh 1*9
9
【練習題】打印結果1+2+3+4+5+6+7+8+9+10=55
腳本內容
[root@mico scripts]# vim count.sh
#!/bin/bashNum=`seq -s + 1 10`
echo $Num=$(($Num))
腳本執行結果
[root@mico scripts]# sh count.sh
1+2+3+4+5+6+7+8+9+10=55
補充說明
shell腳本中批量注釋的方法<<'EOF'
文件內容EOF
或使用 exit
可以注釋其之后的所有內容(類似注釋,實質為不執行后面的內容)