[TOC]
(1)前言
Shell是什么?
答:Shell(壳)本身是一个用C语言编写的程序也是一个是解释执行的脚本语言即命令解释器
,它在操作系统最外层是用户使用Unix/Linux的桥梁,把用户输入解释给操作系统等待操作系统处理后,将结果输出返回给用户,用户的大部分工作都是通过Shell完成的,还是一个功能强大的编程语言,易编写,易调试,灵活性较强
。
它虽然不是Unix/Linux系统内核的一部分,但它调用了系统核心的大部分功能来执行程序、建立文件并以并行的方式协调各个程序的运行。因此,对于用户来说,shell是最重要的实用程序,深入了解和熟练掌握shell的特性极其使用方法,是用好Unix/Linux系统的关键。
WeiyiGeek.shell位置层次
什么是shell脚本? 答:当Linux命令会语句不在命令行下执行(严格的说命令行执行的语句也是shell脚本),而是通过一个脚本程序文件执行时候,该程序就被称为shell脚本或shell程序;与windows中的bat批处理很类似;用户可以在shell脚本中嵌套命令/变量以及流程控制语句从而形成一个功能强大的shell脚本;
Shell是弱类型语言,既是一种命令语言,又是一种程序设计语言,
命令语言
:它交互式地解释和执行用户输入的命令,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序程序设计语言
:它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括变量和流程控制语句为什么要学Shell编程 答:Shell脚本是实现Linux系统及运维自动化管理的重要且必备的工具,尤其是shell脚本擅长处理村文本类型数据,而linux系统中的配置文件万物皆文件,所有极大的方便了我们使用者进行文件处理;几乎每一个合格的Linux系统管理或者运维工程师都需要学习Shell编程;
脚本的优势:
它不需要执行编译过程之后再执行,他是将编译过程放在执行过程中,
所以执行起来要慢得多。Shell有两种执行命令的方式:
我们需要掌握哪一些基础知识?
(2)Shell 发展史
人物介绍:Steve Bourne
也是贝尔实验室的成员,Dennis Ritchie
和 Ken Thompson
的同事,本来他也就是一个默默无闻的码农。但由于 Ritchie 和 Thompson 玩游戏玩出了个 UNIX,一下子整个贝尔实验室都炸开了锅!
刚开始的时候Thompson 写了个简单的程序作为 UNIX 操作系统的接口界面,有了它人类和操作系统就可以进行交流了,叫 shell(我们在 Linux 打开的那个 Terminal 就是一个 shell), Thompson 还给它起了个名字,叫“Thompson shell”(这哥们可真不低调 )Thompson shell 的功能很简单,用户通过它输入一些指定的命令
主人公 Bourne 出现了(逮到机会赶紧上 ),也设计了一个 shell,叫“Bourne shell,sh”
(似乎都生怕别人不知道程序是谁写的 )。
这俩好面子的大牛都力挺自己设计的 shell,渐渐的两种 shell 都有了各自的追随者,渐渐的形成了两大阵营(就像如今的 Vim 和 Emacs)。 Thompson 的粉丝觉得简洁才是真理,Bourne 的死忠则认为实用才是王道!就这样撕逼大战一触即发……
WeiyiGeek.SHELL标准转变
那现在打开的 Terminal 到底是什么 shell?
答:事实上现在大多数 Linux 发行版的默认 Shell 叫 Bash
,它的名字是 Bourne-Again SHell 的缩写,这是关于 Bourne shell(sh)的一个双关语:“Bourne again / born again,Bourne再次/重生”,
由 Brian J. Fox
所编写(看还是低调的哥们赢得了最后的胜利 )。
(3)Shell的分类 由于历史原因,shell有很多不同的版本,而且也有很多有相同功能的命令需要我们进行取舍,以至于代码的规范很难统一。
Shell的两种主要语法类型有Bourne和C这两种语法彼此不兼容,由于Linux的标准Shell是Bash!Bash和sh是相互兼容的。
Bourne Shell
:也称为B Shell,1979年起Unix就开始使用它主文件名为
#sh、ksh、bash、psh、zsh
Bourne shell :sh
Korn Shell : ksh
Bourne Again shell : bash
POSIX shell : psh
C Shell
:主要是BSD版的Unix系统中使用,因其语法和C语言相类似而得名:
#csh、tcsh
c shell : csh
TENEX/TOPS C shell : tcsh
WeiyiGeek.shell分类介绍
Linux支持的shell
echo $SHELL #通过执行echo $SHELL就可以看到当前系统支持的哪种Shell
# cat /etc/shells (脚本文件执行路径) //可以看到当前Linux支持的所有Shell
/bin/sh 或者 /usr/bin/sh #前者实际指向一个文件就是后者
/bin/bash 或者 /usr/bin/bash
/bin/tcsh
/bin/csh
Step1.建立和编写Shell脚本
vi hello.sh #创建一个名为hello的脚本文件,#按`I`插入内容
#first program# #是注释,,会被解释器忽略。可以注释中文
#!/bin/bash #开头必须加上这行注释,脚本才能执行(解释器)建议采用#!/usr/bin/env bash
echo "hello world!" #echo是输出命令
#VIM编辑器,按`ESC`退出编辑,键入`:wq`保存并退出(具体参考vim编辑器的使用)
Step2.赋予执行权限运行脚本直接运行
chmod 755 hello.sh #赋予执行权限 rwxr-xr-x
./hello.sh #`./`用来执行脚本,表示当前目录下,也可以用绝对路径
bash hello.sh #通过bash调用执行脚本
#补充:当然也可以不同赋予权限采用source执行(两种形式)
# File文件默认权限:644 rw-r--r-- ,最大权限是x
# Directory目录默认权限:755 rwxr-xr-x,最大权限是w
source hello.sh
. hellp.sh
#进入shell域退出shell
sh #从Bash进入到了sh
exit #退出当前Shell
#使用bash命令可以在已有的Bash下创建一个子Shell,同样使用exit退出,调用和退出都是一级一级连贯的.
注意事项:
描述:主要学习用户自定义变量、环境变量、语系变量、位置参数变量和预定义变量等变量;变量是PC内存的单元Part,其中修改的值可以改变。
变量的分类: (变量分类从上到下越来越严格)
变量命名规则:
变量名的长度不得超过255个字符
.变量的默认类型都是字符串型
.变量的数据类型:
字符串 CHAR #shell默认类型
# 整型 INT #需要将字符转整形
# 浮点型 Float、Double
# 日期型 Date
运行Shell的时候会同时存在三种变量:
注意事项:
默认变量类型都是字符串类型不含有其他类型
,所以对数字计算时要用特殊方法将字符串转变为数字才能计算。(2)各种括号的作用()、(())、[]、[[]]、{} 描述:为了更好的学习shell中的变量,我们需要先学习各个括号的作用;
1.小括号,圆括号()
括号中的变量不能够被脚本余下的部分使用
。2.双小括号 (( ))
括号中的运算符、表达式符合C语言运算规则
都可用在$((exp))中,甚至是三目运算符。作不同进位(如二进制、八进制、十六进制)运算时,输出结果全都自动转化成了十进制
单纯用 (( )) 也可重定义变量值,比如 a=5; ((a++))可将((a++))可将a 重定义为6
如:echo $((16#5f)) 结果为95 (16进位转十进制),echo $((8#12)) = 10 ,值得学习借鉴;
用于算术运算比较,双括号中的变量可以不使用$符号前缀。括号内支持多个表达式用逗号分开
只要括号中的表达式符合C语言运算规则,比如可以直接使用for((i=0;i<5;i++)),
如果不使用双括号, 则为for i in $(seq 0 4)或者for i in {0..4},再如 if((i<5)),如果不使用双括号,则为if[i<5)),如果不使用双括号,则为if[i -lt 5 ]
3.单中括号,方括号[]
使用-eq,-gt形式
。比较"ab"和"bc":[ ab \< bc ]
,结果为真也就是返回状态为0。4.双中括号,方括号[[]]
使用=~操作符时甚至支持shell的正则表达式
字符串比较时可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真
[[ ]] 中匹配字符串或通配符,不需要引号
使用[[ … ]]条件判断结构,而不是[ … ],能够防止脚本中的许多逻辑错误。
比如:&&、||、<和> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错。
比如:可以直接使用if [[ a != 1 &&a != 1 &&a != 2 ]] 如果不适用双括号, 则为if [ $a -ne 1] && [ $a != 2 ] 或者 if [ $a -ne 1 -a $a != 2 ]
bash把双中括号中的表达式看作一个单独的元素,并返回一个退出状态码。
实际案例:
if [ $i -lt 5 ]
if [ $a -ne 1 -a $a != 2 ]
if [ $a -ne 1] && [ $a != 2 ]
if [[ $a != 1 && $a != 2 ]]
for i in $(seq 0 4);do echo $i;done
for i in `seq 0 4`;do echo $i;done
for i in {0..4};do echo $i;done
for ((i=0;i<5;i++));do echo $i;done
# 执行结果
# 0
# 1
# 2
# 3
# 4
5.大括号、花括号 {}
与小括号中的命令不同,大括号内的命令不会新开一个子shell运行,即脚本余下部分仍可使用括号内变量
。括号内的命令间用分号隔开最后一个也必须有分号
,十分注意:{}的第一个命令和左括号之间必须要有一个空格。#第一种:对大括号中的以逗号分割的文件列表进行拓展。如 touch {a,b}.txt 结果为a.txt b.txt。
#第二种:对大括号中以点点(..)分割的顺序文件列表起拓展作用,如:touch {a..d}.txt 结果为a.txt b.txt c.txt d.txt
# ls {ex1,ex2}.sh
ex1.sh ex2.sh
# ls {ex{1..3},ex4}.sh
ex1.sh ex2.sh ex3.sh ex4.sh
# ls {ex[1-3],ex4}.sh
ex1.sh ex2.sh ex3.sh ex4.sh
#间接引用(值得注意)
var1=1024
var2="var1"
echo ${!var2} #输出1024
# 替换结果
${var:-string} #若变量var为空,则用在命令行中用string来替换${var:-string},否则变量var不为空时,则用变量var的值来替换
${var:=string} #用string替换${var:=string}的同时,把string赋给变量var,常用模式
${var:+string} #替换规则和上面的相反,即只有当var不是空的时候才替换成string,若var为空时则不替换或者说是替换成变量 var的值,即空值。(因为变量var此时为空,所以这两种说法是等价的)
${var:?string} #替换规则为:若变量var不为空,则用变量var的值来替换${var:?string};若变量var为空,则把string输出到标准错误中,并从脚本中退出。我们可利用此特性来检查是否设置了变量的值。
# 匹配结果
${var%pattern} #shell在variable中查找,看它是否一给的模式pattern结尾,如果是,就从命令行把variable中的内容去掉 右边最短 的匹配模式
${var%%pattern} #如果是,就从命令行把把variable中的内容去掉 右边最长 的匹配模式
${var#pattern} #如果是,就从命令行把variable中的内容去掉 左边 最短的匹配模式
${var##pattern} #如果是,就从命令行把variable中的内容去掉 左边 最长的匹配模式
# 字符串提取和替换
${var:num} #shell在var中提取第num个字符到末尾的所有字符
# 若num为正数,从左边0处开始
# 若num为负数,从右边开始提取字串,但必须使用在冒号后面加空格或一个数字或整个num加上括号,如${var: -2}、${var: 1-3}(注意这里)或${var:(-2)}。
${var:num1:num2} #num1是位置/num2是长度。表示从$var字符串的第$num1个位置开始提取长度为$num2的子串,不能为负数。
${var/pattern/pattern} #表示将var字符串的第一个匹配的pattern替换为另一个pattern。
${var//pattern/pattern} #表示将var字符串中的所有能匹配的pattern替换为另一个pattern。
这四种模式中都不会改变variable的值,其中只有在pattern中使用了*匹配符号时,%和%%,#和##才有区别。
结构中的pattern支持通配符,*表示零个或多个任意字符,?表示仅与一个任意字符匹配,[…]表示匹配中括号里面的字符,[!…]表示不匹配中括号里面的字符 (基础正则)。
实际案例:
示例1、shell变量替换
ZBX_MAIN_DB=${ZBX_MAIN_DB:-"mysql"} #如果ZBX_MAIN_DB变量为空则采用mysql进行替换
$echo $ZBX_MAIN_DB #mysql
示例2、shell变量匹配
var=testcase
$echo $var
testcase
$echo ${var%s*e} #去除右边 最短 匹配
testca
$echo ${var%%s*e} #去除右边 最长 匹配
te
$echo ${var#?e} # 去掉 左边 最短的匹配模式
stcase
$echo ${var##?e}
stcase
$echo ${var##*e} # 去掉 左边 最长的匹配模式
$echo ${var##*s}
e
$echo ${var##test}
case
var=/home/centos
echo $var
/home/centos
echo ${var:5} #取子字符串
/centos
echo ${var: -6}
centos
echo ${var:(-6)}
centos
echo ${var:1:4} #注意字符是从0开始,这个案例是表示从1开始偏移后面的4位数;
home
echo ${var/o/h} #匹配从左到右开始的第一个字符并将其替换
/hhme/centos
echo ${var//o/h} #完全匹配替换
/hhme/cenths
6.符号$后的括号以及大括号
exprexpression
效果相同, 计算数学表达式exp的数值, 其中exp只要符合C语言的运算规则即可, 甚至三目运算符和逻辑表达式都可以计算。实际案例:
#示例1.建议采用${变量形式},推荐给所有变量加上花括号这是个好的编程习惯
#如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了
$ for skill in Ada Coffe Action Java
> do
> echo "I am good at ${skill}Script"
> done
# I am good at AdaScript
# I am good at CoffeScript
# I am good at ActionScript
# I am good at JavaScript
#示例2.拼接字符串
yourname="test"
greeting="hello,"$yourname"!"
greeting1="hello,${yourname}!" #建议形式
echo $greeting;echo greeting1
# hello,test!
# hello,test!
#示例3.字符串长度 ${#test}、 取子字符串${test:1:4}(上面已经说了)
test=linux123456linux
echo ${#test} #统计字符个数
# 16
string="alibaba is a great company" #提取字符串
echo ${string:0:5} #输出aliba , 注意是:号 【从0开始后面的五位字符】
echo `expr index "$string" o` #输出字符串索引位置 【从1开始记】
补充:单个小括号与单个大括号多条命令执行
(cmd1;cmd2;cmd3)
新开一个子shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开, 最后一个命令后可以没有分号。{ cmd1;cmd2;cmd3;}
在当前shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开, 最后一个命令后必须有分号, 第一条命令和左括号之间必须用空格隔开
。字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似
,识别变量和不识别变量。
符号 | 作用 |
---|---|
‘ ‘ | 单引号:在单引号中所有的特殊符号,如’$’和’`’(反引号)都没有特殊含义 |
“ “ | 双引号:在双引号中特殊符号都没有特殊含义,但是”$”、”`”和”\”是例外,拥有”调用变量的值”、”引用命令”和”转义符”的特殊含义 |
`id` | 插入的命令可以直接执行 |
$() | 和反引号一样,用来引用系统命令 |
$ | 用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。 |
\ | 转义符,跟在\之后的特殊符号将失去特殊含义,变味普通字符。如\$将输出”$”符号,而不当作是变量引用 |
实际案例:
#单引号
- 任何字符都会原样输出,所以单引号字符串中的变量是无效的;
- 单引号字串中不能出现单引号(对单引号使用转义字符后也不不行);
str='this is a string $var'
#双引号
- 可以解析变量并且可以出现转义字符
str1="this is a variable $test"
#示例1.Shell变量声明和调用
变量名=变量值
echo $变量名
test=12346
echo "$test" # $调用变量 123456
echo $str #变量不被解析 this is a string $var
echo $str1 #变量被解析 this is a variable 123456
#示例2.命令替换
$echo `id`
$echo $(id) #这样执行的命令不会换行
uid=0(root) gid=0(root) 组=0(root)
#常用方式
aa=$(who)
echo "$aa" ${aa}
#示例3.变量叠加
x=123
x="$x"456
echo $x # 则x变成123456
WeiyiGeek.单双引号
不同变量类型申明:
#示例1.declare与set和unset联合使用
$declare TPATH='Visual C++ -- JAVA'
$set -a TPATH #设置环境变量
$env | grep "TPATH"
TPATH=Visual C++ -- JAVA #写入了env环境变量,变量名大写
$unset -v TPATH #删除tpath变量
$env | grep "TPATH"
#数组整型
# -a 数组类型
# -i 整数
# -x 环境变量
# -r 只读变量
# -p 显示类型
declare -a test=0
declare -a test[1]=1
declare -a test[2]=2
$echo ${test[1]} ${test[2]}
echo ${test[*]}
#类型查看
declare -i test=1024
$declare -p test
# declare -i test="1024"
注意事项:
变量名和等号之间不能有空格
,这可能和你熟悉的所有编程语言都不一样.只在当前shell中生效,临时
)对系统生效的环境变量名和变量作用是固定的一般大写
(当前父shell和所有子shell中生效)环境变量配置文件:
全局配置文件:/etc/profile
用户配置文件:~/.bash_profile
注销时生效的环境变量配置:~/.bash_logout
历史操作:~/.bash_history
#重新加载bash配置文件:
source .bashrc #立即生效
系统启动加载Shell环境顺序图:
WeiyiGeek.Shell环境顺序图
设置环境变量的方法:
#示例1:
declare -x T1="demo"
set -a T2="demo"
export T3="demo"
查看环境变量:
env #env命令用于显示系统中已存在的环境变量,以及在定义的环境中执行指令。
# XDG_SESSION_ID=60
# HOSTNAME=master
# TERM=xterm #终端环境
# SHELL=/bin/bash
# HISTSIZE=1000
# SSH_CLIENT=10.20.172.108 51849 22 #当前SSH连接信息
# SSH_TTY=/dev/pts/1 #ssh连接的终端时pts/1
# USER=root
$LOGNAME #登录用户相关信息
$UID
$Shell
$HOME #家目录
$PWD
$PATH #用户所输入的命令是在哪些目录中查找
$PS1
$PS2
$RANDOM #随机数 (重要)
在PATH环境变量
中Linux中执行可执行文件常用的方法是输入绝对路径,但是如果不输入路径时,系统会在PATH中的路径中寻找该可执行文件,直到找到该指定文件,就执行,但是找不到时就报错。
直接输入文件名就能执行自定义脚本
echo $PATH #系统搜索命令的路径,路径之间用:分割。
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
#增加PATH环境变量(拼接即可),tab补全命令也是按照PATH中的来补全
PATH="$PATH":/root/sh
WeiyiGeek.
下面就是设置命令行显示格式对于有强迫症的患者来说是极大的好处,可以随心所欲的设置$PS1变量
;
set | grep PS1
#PS1='[\u@\h \W]\$ ' ==>> [root@master ~]#
[root@WeiyiGeek ~] # [当前用户的账号名称@主机名的第一个名字 工作目录的最后一层目录名]#
#PS1的常用参数以及含义:
\d :代表日期,格式为weekday month date,例如:"Mon Aug 1"
\H :完整的主机名称
\h :仅取主机名中的第一个名字
\t :显示时间为24小时格式,如:HH:MM:SS
\T :显示时间为12小时格式
\A :显示时间为24小时格式:HH:MM
\u :当前用户的账号名称
\v :BASH的版本信息
\w :完整的工作目录名称
\W :利用basename取得工作目录名称,只显示最后一个目录名
\# :下达的第几个命令
\$ :提示字符,如果是root用户,提示符为 # ,普通用户则为 $
# 在PS1中设置字符颜色的格式为:
\[\e[F;Bm\] 其中“F“为字体颜色编号为30-37,“B”为背景颜色,编号为40-47。
WeiyiGeek.Front-backgroudcolor
#设置命令行的格式为绿字黑底(\[\e[32;40m\]),显示当前用户的账号名称(\u)、主机的第一个名字(\h)、完整的当前工作目录名称(\w)、24小时格式时间(\t)
PS1='[\[\e[32;40m\]\u@\h \w \t]\$ '
#经过多次测试后,最终确定了一个适合我自己的格式
#Centos
vim .bashrc ##设置永久生效,修改.bashrc文件,加入PS1
PS1="[\[\e[32;40m\]\u\[\e[37;40m\]@\h \[\e[31;40m\]\w \[\e[43;40m\]\t\[\e[0m\]]\\$ "
source .bashrc #立即生效
#Debian:
/etc/bash.bashrc
PS1='${debian_chroot:+($debian_chroot)}\[\e[32;40m\]\u\[\e[37;40m\]@\h:\[\e[34;40m\]\w \[\e[31;40m\]\A\[\e[0m\]\$ '
还有一个重要的环境变量就是语序环境变量
,我们在讲解locale命令的时候是有所涉及的,就是$LANG定义系统主语系的变量
;
#输出当前语系:
echo $LANG
zh_CN.UTF-8
#查看系统可支持的语系:
locale -a | more
LC_ALL : 定义整体语系变量
#设置语系:(临时)
LANG=en_US.UTF-8 #英文
LANG=zh_CN.UTF-8 #中文
#永久设置:
语系信息放在文件 /etc/sysconfig/i18n 下(`Centos 7以下版本才有`)下次开机以后的系统环境,然后你需要做的就是找到LANG 和 SUPPORTED 这两行,然后将以下内容对应着替换上
LANG="zh_CN.UTF-8"
SUPPORTED="ZH_CN.UTF-8:ZH_CN:ZH"
WeiyiGeek.语言环境
如果没有图形界面还非要显示中文的两种方法:
rpm -ql zhcon.rpm #查询
rpm -ivh zhcon.rpm #安装
rpm -e zhcon.rpm #卸载
内部字段分隔符变量IFS
描述:internal field separator,IFS 是shell脚本中的一个特殊变量在处理文本数据时很有用。
把单个数据流划分成不同的数据元素的定界符,内部字段分隔符就是用于特定用途的定界符。
IFS是存储定界符的环境变量,是shell环境中的默认定界符字符串,默认值为空白字符(换行符、制表符、空格)
$ echo $IFS
实际案例:
#!/bin/bash
#迭代一个字符串或者CSV(comma separated value,都好分隔型数值)中的单词:
data="111,222,333|444,555,666"
oldIFS=$IFS #定义一个变量为默认IFS
IFS='|' #设置IFS为逗号
for i in $data # date的取值列表
do
echo S:$i
done
IFS=$oldIFS #还原IFS为默认值
#执行结果
S:111
S:222
S:333
S:444
S:555
S:666
#如果IFS为1
S:111,222,333
S:444,555,666
IFS被设置为逗号’,’ , shell将逗号解释为一个定界符,因此变量$i在每次迭代中读取由逗号分隔的字符串作为变量值
主要是向脚本文件中传递脚本运算需要的值,更适合给程序的编写者使用,在我们写可变参数脚本尤其有用的;
主要位置参数变量:
${n} : $0代表脚本本身,$0~9代表第一个到底九个参数,如果是9个以上参数需要使用大括号包含 ${10}
${*} :代表命令行中的所有参数,它把所有参数 看成一个整体
${@} :代表命令行中的所有参数,它把所有参数 区分对待(划分个体)
${#} :代表命令行中所有参数的个数;
实际案例:
#说明:${0} 或者 $0 就是输出文件本身,从命令行中输入得参数$1/9,${10}
#!/usr/bin/env bash
echo "文件名称:" ${0} $0
basename ${0}
num1=$1;num2=$2
let num3=$num1+$num2
echo -e "$num1 + $num2 = $num3 \n"
for i in `seq $num3`;do
echo "for output: $i"
done
echo -e "\n"
#其他三个特色是字符(区别)
echo "A total of $# Parameters" #代表所有参数的个数
echo "Every Paraments is:$*" #代表所有参数一个整体
echo -e "The Paraments is:$@ \n" #用这个代表各个参数的值(个体)
#整体和个体的区别
for i in "$*";do
echo "整体 output:$i" #整体
done
for j in "$@";do
echo "个体 output:$j" #个体
done
#执行结果
# 文件名称: ./var.sh ./var.sh
# var.sh
# 1 + 2 = 3
# for output: 1
# for output: 2
# for output: 3
# A total of 2 Parameters
# Every Paraments is:1 2
# The Paraments is:1 2
# 整体 output:1 2
# 个体 output:1
# 个体 output:2
shift 迁移语句描述:用于迁移位置变量,将1~9依次向左传递;通常用于在不知道传入参数个数的情况下依次遍历每个参数然后进行相应处理(常见于Linux中各种程序的启动脚本)。例如当shell程序处理完前九个命令行参数后,可以使用shift 9命令把10移到1;并且位置参数熟料也会随之而变化;
简单说明:
#例如:若当前脚本程序获得的位置变量如下:
$1=file1、$2=file2、$3=file3、$4=file4
#执行一次shift命令后,各位置变量为:
$2=file2、$3=file3、$4=file4
#在执行一次:
$3=file3、$4=file4
实际案例:
#示例1:依次读取输入的参数并打印参数个数
#每次运行shift(不带参数的),销毁一个参数,后面的参数前移
#!/bin/bash
while [ $# != 0 ]
do
echo "prama is $1,prama size is $#"
shift
done
#输入如下命令运行:
./shift_test.sh a b c
prama is a,prama size is 3
prama is b,prama size is 2
prama is c,prama size is 1
#示例2.同理shift n后,前n位参数都会被销毁,比如:
echo "参数个数为:$#,其中:"
for i in $(seq 1 $#)
do
eval j=\$$i
echo "第$i个参数($"$i"):$j"
done
shift 3
echo "执行shift 3操作后:"
echo "参数个数为:$#,其中:"
for i in $(seq 1 $#)
do
#通过eval把i变量的值($i)作为变量j的名字
eval j=\$$i
echo "第$i个参数($"$i"):$j"
done
#输出结果为:
参数个数为:5,其中:
第1个参数($1):a
第2个参数($2):b
第3个参数($3):c
第4个参数($4):d
第5个参数($5):e
#执行shift 3操作后:
参数个数为:2,其中:
第1个参数($1):d
第2个参数($2):e
描述:该变量在发挥着及其重要的角色特别是在Shell编程的时候;
符号 | 作用 |
---|---|
$? | 检测上一个命令的返回值,判断是不是执行成功,是则为0不是则不为0 |
$$ | 当前 Shell 进程的 pid |
$! | :上一个后台进程的 pid 可以使用这两个指令来获取相应的进程 pid |
实际案例:
#示例1.上一个进程执行情况
echo $?;text=`echo $?`;
echo $text
0
# bash: 0: 正确执行
# bash: 127(random): 没正确执行(非0)
#示例2.当前进程号
$ echo $$
3672
$ ps aux | grep "3672"
root 3672 0.0 0.2 116040 2676 pts/1 Ss 21:28 0:00 -bash
#示例3.例如,如果需要获取某个正在执行的进程的 pid(并写入指定的文件)
date >> /tmp/txt.log &
[1] 2795 #上一个进程号pid
echo $!
2795
WeiyiGeek.当前进程PID
总结事项:
描述:在Shell高级编程中可以采用eval命令进行变量间的间接引用,eval命令会多次扫描自己的参数(变量)并且运行;些需要进行两次扫描的变量有时候被称为复杂变量。
基础示例:
#示例1.间接引用变量
vara=varb
varb="ls -lah" #注意这里双引号很重要
eval \$$vara #注意转义字符 == 执行了 ls -alh
-rwxr-xr-x 2 Administrator 197121 39K 九月 11 2018 git.exe
-rwxr-xr-x 1 Administrator 197121 145K 九月 11 2018 git-gui.exe
#实例2.间接引用变量进行输出
var=varc
varc="weiyigeek"
eval vard=\$$var
echo "Name: ${vard}" #Name: weiyigeek
#示例3.变量间接调用之 ${!}
x=var
var=1024
${!x}
shell脚本案例:
#!/bin/bash
# 实现变量的间接引用
BLACK='\E[1;30m'
RED='\E[1;31m'
GREEN='\E[1;32m'
YEELOW='\E[1;33m'
BLUE='\E[1;34m'
PINK='\E[1;35m'
CYAN='\E[1;36m'
WHITE='\E[1;37m'
RES='\E[0m'
#关键点1
function demo()
{
#获得最后一个参数
echo "Last argument is $(eval echo \$$#)"
echo "Last argument is $(eval echo $#)"
}
function colorPrint()
{
eval COLOR=\$$1
echo -e "${COLOR}${2}${RES}"
}
#关键点2
function usage()
{
echo -e "${RED}Usage:$0 COLOR=[BLACK|RED|YELLOW|BLUE|PINK|CYAN|WHITE] [CONTENT]${RES}"
exit 1
}
#判断参数个数 2 > 2 | $# 从0 开始 计数
[ 2 -gt $# ] && usage
demo $*
colorPrint $*
执行结果:
#脚本帮助
Usage:eval.sh COLOR=[BLACK|RED|YELLOW|BLUE|PINK|CYAN|WHITE] [CONTENT]
WeiyiGeek.eval变量间接引用
描述:在写shell的时候常常利用执行命令后返回结果作为参考或者进行判断,可以说是非常的常用;
命令的执行结果重定向到变量的几种方式
#示例1:
var1=$(command)
var3=$(command 2>&1) #要将stdout和stderr保存到变量,需要在结尾处添加2>&1。
#示例2
var2=`command`
$- : 记录着当前设置的shell选项,可通过set命令进行修改;
echo $-
himBH
#5个字母分别有各自含义
* h:hashall 将命令所在的路径记录下来避免每次都要查询。#举例:当h选项开启时,如果将某个自定义命令从/usr/bin/目录下移动到/usr/local/bin/再运行,会提示无此命令。而当通过set +h将h选项关闭后,上述情况就不会出现。
* i:interactive-comments 当前的 shell 是一个交互式的
* m:monitor 打开监控模式就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。
* B:braceexpand 大括号扩展。`cp A_File{,.back_up}`
* H:history 执行过的命令将被记录下来,然后再用户登出的时候保存再用户家目录下的bash_history中;
_ (类比\!\): 记录上一个命令执行的参数(最后一个参数前面以空格进行分割否则整体)
#案例1:
$mkdir -p /k8s/kubernetes/ssl/ && cd $_ && pwd
/k8s/kubernetes/ssl/
#案例2:
echo "WeiyiGeek" && echo $_
WeiyiGeek
WeiyiGeek