Shell编程

Shell编程语言为高级程序设计语言,解释型语言

Shell的任务:

分析命令;处理通配符,重定向,管道和作业控制;搜索命令并执行

Linux中常见的Shell脚本:

Bourne Shell(/usr/bin/sh或者/bin/sh) Unix标准的shell

Bourne Again Shell(/bin/bash) Linux标准的shell

C Shell

zsh

Shell一般由管理员在创建用户时,为用户指定,保存在/etc/passwd文件中,可以通过usermod更改。

Linux系统中一般默认使用bash,在编写脚本文件时,需指明bash为脚本解释器

#! /bin/bash   
# (#!)为幻数,必须在第一行。通知系统采用bash解释
echo "Hello World"

注释

单行注释

# 此处为一行注释
echo "hhh" #注释从此处到一行结束

多行注释

:<< EOF
nu 1
nu 2
nu 3
EOF

:<< 字符串 该注释表示注释到该字符串再次出现时,通常为EOF,但也可以是a,abs,esdfsf等任意字符串,前后一致即可注释

运行方法

1. chmod +x test.sh #给当前脚本文件执行权限
   ./test.sh        #当前目录下执行脚本文件
   /etc/home/test.sh #采用绝对路径执行脚本文件
2. bash test.sh [参数] #运行解释器运行,可以将参数传递进入test.sh脚本文件中
3. export PATH=/bin:$PATH #将/bin加入到整个环境变量中
   test.sh          #在任意目录下可执行该脚本文件

元字符

元字符功能
回车换行输入命令后要按回车
TAB/空格命令行中分隔符
#一行注释
“[双引号]引用多个字符并允许替换
'[单引号]引用多个字符,完全不变
`[反引号]替换命令,即函数转化
$一行的结束,或引用变量
&使命令在后台运行
()使命令在子shell中运行
[]匹配[]中的一个字符
{}在当前shell中执行,或实现扩展
*匹配0个或多个字符
?匹配单个字符
^紧跟^后的字符开始的行,或否定
|管道命令的符号
;顺序命令的分隔符
<,>输入,输出重定向
/根目录或路径分隔符
\转义字符;或者续行符
!启动历史记录列表中的命令与当前命令
%指定一个作业号作为起始字符
~表示主目录

注意点

三大引号:

单引号:‘abs"sf"\t’ 输出: abs"sf"\t

双引号:“ab\tc\\” 输出: ab c\ 转化:对“\”转义,对"$" 替换,对“`”进行函数执行

反引号:`date` 输出:2022年 3月 18日 …… 转化:对函数进行执行

命令的顺序执行

  1. command1;command2;command3;…… 顺序执行命令,若是命令后用&替换;则表示两侧的命令并发执行
  2. &&: command1&&command2&&…… 顺序执行,若是有command返回false,则后续的&&命令终止执行
  3. ||:command1||command2||…… 顺序执行,若是有command返回true,则后续的||命令终止执行

子shell环境

在()中运行的命令在子shell中执行,不影响当前环境

同样的,采用【bash 脚本】运行文件,文件在子shell中运行,不影响当前的环境

变量

shell变量分为两类:一是标准shell变量,用户登录时Shell为建立用户环境而注册的变量,也称环境变量;另一类是用户自己定义的变量,即用户变量(临时变量)。

用户变量

变量命名

以字母或下划线开始,后可以是字母、数字和下划线,注意区分大小写。

变量赋值

变量名=值 【赋值号左右没有空格】

变量引用

#! /bin/bash
a="/e"     	#变量声明与赋值【初始化】
echo $a		#输出变量的值,用$替换
echo "$a is value of \$a"
cd "${a}tc/home"	#当变量后非分隔符时,采用花括号替换

变量替换

操作符描述
${var}返回var的值,若没初始化则返回空
${var:-str}若var存在且不为空则返回var的值,否则返回str
${var:=str}若var存在且不为空则返回var的值,否则将str赋给var然后返回
${var:?str}若var存在且不为空则返回var的值,否则显示:var:str
${var:+str}若var存在且不为空则返回str,否则返回空
${#var}返回var的值的长度
${var%str}从var的尾部开始删除匹配str的最小部分
${var%%str}从var的尾部开始删除匹配str的最大部分
${var#str}从var的头部开始删除匹配str的最小部分
${var##str}从var的头部开始删除匹配str的最大部分

注意:使用最大匹配时,要结合“*”使用,如:${var%%4*}

数组变量

声明

array[key]=value					#直接给下标为key的array数组元素赋值
array=( value1 value2 value3 ……)	#初始化数组array,从0开始逐个赋值
array=( [1]=one [2]=two [3]=three)	#指定下标赋值

数组访问

${array[key]}	#引用array[key]中值
${array[*/@]}	#引用array所有元素值,*扩展为一个词,@扩展为多个词,以空格隔开

数组删除

unset array[1]	#删除数组中的第一个元素
unset array		#删除整个array数组

计算数组大小

${#array}		#等效于${#array[0]},按字节计算数组第0个元素的大小
${#array[@]}	#计算数组中的元素个数

@与*

#! /bin/bash
ns=(max san [6]=zhang [35]=lsd)
a=("${ns[@]}")	#元素逐个复制
echo ${a[0]}	#max
b=("${ns[*]}")	#所有元素当成一个元素复制到新数组
echo ${b[0]}	#max san zhang lsd

环境变量

环境变量是Shell为了维护用户的使用环境而定义的一些变量。Linux环境(Shell环境)由许多变量组成,这些变量决定了用户环境的外观。

环境变量与用户变量的区别

  1. 环境变量可以被当前Shell下启动的子进程(子Shell) 所继承【拷贝】,而局部(用户)变量不被继承。
  2. 子进程的环境变量独立于父进程,子进程中对环境变量的修改不影响父进程变量的值。
环境变量说明
$HOME用户的注册目录(用户主目录),即~
$SHELL设置用户的shell类型
$USER保存当前的用户名
$PATH查找命令的目录列表,目录名用冒号隔开
$PWD保存用户当前在文件系统的位置
$PS1定义shell的主提示符
$PS2定义shell的副提示符
$IFS域内分隔符
注意

$PATH:在按照列表中目录顺序查找时,在前一个目录中查找成功后,终止查找

$PS1:主提示符即$,#(代表普通用户,root用户)

$PS2:副提示符即命令不完整时的">"符号,可继续填写命令

$IFS:分隔符为空白【空格,跳格,换行】

变量导出——export

将用户变量导出为环境变量

#! /bin/bash
echo $a
a=20
echo $a

当前环境变量中没有a,则输出结果为


20

当调用命令

export a=12

此时,a为环境变量,脚本输出结果为

12
20

但是程序在子shell环境中运行,并不会改变环境变量的值,若在命令行中输出a,值仍为12。

位置参数

位置参数及引用

位置参数也称为命令行参数,即:组成命令行的所有元素。
在Shell脚本中通过$0、$1…进行引用位置参数

输入输出

echo

echo [option] [string]

将string的内容按照option指定的方法送到屏幕上进行显示
option:

-n 输出后不换行

-e 对字符串中的特殊字符进行翻译特殊字符

​ \n 换行

​ \t 跳格

read

read [options] 变量列表

常用选项:

-a name 把词读入name数组中

-e 把一整行读入第一个变量中,其余变量为NULL

注意:读入的一行若由若干词组成,以空格或者制表符隔开。以换行符结束读入,若词的数目大于变量数,则把余下的数都放入最后一个变量;若词的数目小于变量数,则后面的变量为NULL。

测试

表达式exp可以用test exp或者[exp]来检测,这个命令检测一个表达式并返回true还是false.

  1. test [expression]
  2. [ [ expression ] ] 内括号表示可选,外括号表示test,操作数和操作符以及括号之间至少留一个空格

test命令可以使用的条件类型有三类:

  1. 字符串比较
  2. 算术比较
  3. 文件有关的条件测试

字符串比较:

​ -z str 字符串长度等于0
​ -n str 字符串长度不等于0
​ str1 = str2 str1与str2 相等
​ str1 != str2 str1与str2 不相等

算术与逻辑比较

符号含义
-eq=
-gt>
-ge>=
-ne!=
-lt<
-le<=
!not
-oor
-aand

应用:exp1 [符号] exp2

shell程序中的**$#得到参数个数**

文件检查

-f 普通文件

-d 目录

-L/h 符号链接文件

-s 文件大小非零

-r 可读 -w 可写 -x 可执行

结构控制语句

分支语句

if-then-elif-else-fi语句

if常用于二路跳转,但也同样可以用于多路跳转。

格式如下:

第一种

if expression

​ then

​ then-command

fi

第二种

if expression

​ then

​ command-list

else

​ command-list

fi

第三种

if expression1

​ then

​ then1-commands

elif expression2

​ then

​ then2-commands

else

​ else-commands

fi

当条件expression为真时,执行then后的语句,否则执行elif,else,fi之后的语句。

例1:读入一个目录或者一个文件名,输出目录中文件名字或者文件内容。

#! /bin/bash
echo -n "Please enter the directory name or file name:"
read fname
if [ -d $fname ]
	then
	ls $fname
elif [ -f $fname ]
	then
	cat $fname
else
	echo "input error!"
fi

例2:读入一个单词,存入一个文件中。

#!/bin/bash
echo -n enter filename:	#不换行输出
read  fname		#读入文件名字
echo enter words:
read words		#读入单词
echo $words 2>/dev/null >$fname	
#2>/dev/null:忽略错误信息
#>$fname:重定向存入文件
if [ $? –eq 0 ]	
#$?返回上一次命令执行的成功或者失败的状态。如果成功就是0,失败为1.。
#-eq:逻辑判断 =
    then
	echo Write successfully
else
	echo write error
fi

case语句:

case word in		#以两个分号表示结束:;;
	pattern1)	command_list;;	#pattern可以用竖线分隔多个模式
	pattern2) 	command_list;;	#模式使用shell的文件名匹配规则。
	[*)    	command_list;;]	#*)表示匹配任意word,即其它选项之意
esac

在if嵌套超过三层后建议使用case语句。

例:输入yes或者y表示是早上,输入no或者n表示是晚上。

#! /bin/bash
echo -n "Is it morning? Please answer yes or no:"
read answer
case "$answer" in		#此处缩进可以不要
	yes ) echo "Good Morning"
	;;
	no ) echo "Good Afternoon"
	;;
	y ) echo "Good Morning"
	;;
	n ) echo "Good Afternoon"
	;;
	* ) echo "Sorry, answer not recognized"
	;;
	esac		# 以esac表示case语句结束
exit 0

循环语句

for语法1:

for 变量 [ in 变量列表]

do

command-list

done

变量列表中的变量逐个赋值给变量,然后对应执行一次循环体。

#! /bin/bash
for foo in 1 fesa 3 effe 5 6fes 7
do
	echo "$foo"
done
exit 0

例1:将data中的词逐行输出。

#! /bin/bash
data="a,b,c,d"
IFSBAK=$IFS		#IFS分隔符,内置环境变量,存储默认的文本分隔符
IFS=,			#设置分隔符为,
for item in $data
do
	echo Item: $item
done
IFS=$IFSBAK		#还原
for item in $data
do
	echo Item: $data
done

运行结果

Item: a

Item: b

Item: c

Item: d

Item: a,b,c,d

例2:备份指定目录中所有文件到指定目录

#!/bin/bash 
# -d $1:判断第一个参数是否是目录,是返回真,不是返回假
# ! -d $1:对结果取反
# 一行中程序顺序执行以分号“;”分隔语句,等同于换行
[ ! -d $1 -o ! -d $2 ]&&{ echo arguments error;exit 1;}
for file in $1/* 
do
	 if test -f $file –a –r $file #文件是普通文件,且可读则复制
	     then
		 cp $file $2 
	fi	#if语句结束标识
done

for语法2: (同C语言)

for ((e1;e2;e3))
do    
	命令列表
done

例:应用bc命令进行表达式1+1/2+1/3+····1/n计算,结果保留三位小数。

bc命令是任意精度计算器语言,通常在linux下当计算器用。

#! /bin/bash
read n
total=0.000
an=0.000
for(( num=1;num<$n;num++ ))
do
        i=$num
        if [ $i != 0 ]
        then
   #通过管道符,将输出的命令作为bc的输入,scale设置精度,而后的语句则在bc中运算
                an=`echo "scale=3;1.000/$i" | bc`       #计算1/n,保留三位小数
                total=`echo "scale=3;$total+$an" | bc`  #计算total
        fi
done
echo $total

while语句:

while command
do
	command-list
done

例:登陆程序

#!/bin/sh
count=3
while [ $count -gt 0 ]	#count > 0时执行循环
do
	echo -en "Login name:"
	read logname
	echo -en "Password:"
	read pwd
    #用户为root,密码是12345
    [ $logname = "root" -a $pwd = "12345" ] && {
		echo "Welcome Here!!!"
		count=0
	}
	let count=count - 1	#计数减一
    sleep 5 	#休眠5ms
done

break、continue、exit

break 退出循环
continue 跳过本次循环继续
exit [n] 退出脚本,并设置退出码

退出码说明
0成功
1~125错误代码[特定含义]
126文件不可执行
127命令未找到
127以上出现一个信号(自定)

函数

function_name() {  
	command_list
	[return n]	#return用于带回函数的返回码(0~255)
}

函数必须先定义后使用

函数在当前环境下运行,和调用它的脚本共享变量;而且可以通过把变量作为定位参数来赋值的方式传递变量。local设置函数体内的临时变量

#! /bin/bash
txt="global varible"
foo() {
	local txt="local varible"	#定义临时变量
	echo "Function foo is executing"
	echo $txt
}

echo "script starting"
foo()
echo $txt
exit 0

运行结果

script starting

Function foo is executing

local varible

global varible

数值处理

let命令

let 赋值表达式

使用C的表达式语法;

在表达式中引用变量时,前可不加“$”;

按长整形求值,不检查溢出。

表达式中有特殊字符时,须用双引号括起来。

算术运算符含义
-+一元运算(正负)
!~逻辑非,补
**指数
* / %乘,除,取模
+ -加减
<< >>左移,右移
>,<,<=,>=大小判断
==,!=判等
else其余同C中语法

$((……))和$(……):两对圆括号表示算术替换,一对圆括号表示命令执行。

例:统计文件中的行数

num=$(( $(wc -l < letter.txt)/100 + 1 ))

echo $num

shell脚本跟踪与调试

通过以下方法进行调试:

bash -nvx 脚本名 #执行脚本
-n 仅做语法检查
-x 显示对命令行完成参数替换后的结果。(跟踪模式)
-v 在执行命令前将命令行原样输出一次

也可以将以上参数加到脚本幻数之后,如:
#!/bin/bash -x

Logo

更多推荐