Shell脚本简单入门教程,如果你分不清shell是什么,此文一定不适合你。毕竟只花了十多分钟看的教程,不会涉及细节,细节将在以后的使用中逐渐以示例形式给出。
PS:借来的东西,果然迟早是要还的。刚在linux下学习时,为了批量处理,当时会C语言,就用C语言编个程序实现,心想虽然不会shell,不是也很方便嘛。然后会了C++,发现比C用起来方便多了。再然后越来越的批处理工作需要程序,C++有点太大杀气了,Python这么火,而且也很强大,学了肯定不白学,遂各种自动化的工作就交给了Python。然后最近发现Python这货也有点太大杀所了,我只是想写个简单的自动化指令而已。所以现在来还当年欠下的shell。假如你也如此,还是花上几十分钟,掌握一把小刀,何乐而不为呢。
关于
记得脚本最前面添加shell解释器,通常为sh、bash,其他的不常见,依然先给个HelloWorld
#!/bin/sh
echo "HelloWorld" #我是注释
然后给了执行权限即可直接执行,或者手动通过sh script.sh来执行,shell脚本后缀通常为.sh。当然Linux下面文件只要有了可执行权限(chmod +x file),什么后缀都没有影响。
我们平时使用的terminal其实就是个shell解释器的交互窗口,所以学习shell编程直接开个terminal就可以开编了。shell中可以直接执行任何linux/unix命令(其实这些命令就是shell内部的)正是它方便之处
变量
shell变量不分类型,默认都是字符串。单引号的字符串类似raw字符串,不能解析转义字符及变量。不被引号引住的字符串中不能用空格。
变量定义形如:变量名=值,等号中间不能有空格。变量名要求即编程语言的常规要求
变量引用则直接在变量名前加$符号,为了限定shell解释器解释变量名的边界可以用大括号包围变量名:${变量名}
ex:
var="maxwi"e
cho "Hello ${var}.com"
字符串
在shell的语句中间执行命令只需要将命令放在”`”(ESC下面那个键)之间即可,当然任何命令都可以在命令前面没有内容时直接执行:
cd $HOME;ls #进入个人目录并执行ls
var=`ls` #ls结果以空格分隔的字符串返回给var
for file in $var # for...in循环输出以空格分隔的字符串内容
do
echo $filed
one
多个命令通过分号(;)并入一行执行
只读变量
var=8
readonly var
var=9 #出错
删除变量
unset var
拼接字符直接将其放在一起即可
a=I
b='love'
c="China"
d="$a $b $c"
echo $d
输出:I love China
获取字符串长
echo ${#var}
字符串切片
echo ${var:2:5}
注意字符串中字符是从0计数,上述切片内容是从第3个字符到第6个字符,包含位置2和5位置上的字符
数组
数组下标从0开始,使用小括号(())定义数组,元素以空格或回车分隔:arr=(val1, val2, val3)
使用下标获取相应元素:${arr[index]}
获取数组中的所有元素echo ${arr[@]}或echo ${arr[*]}
获取数组长度:
echo ${#arr[@]}
当然里面可以是*
for...in循环可以用于遍历数组中的元素
var=(1 2 3 I 'love' "China")
for v in ${var[@]}
do
str="$str $v"
done
echo $str
输出:
1 2 3 I love China
命令行参数
通过$n来访问传递给shell脚本的命令行参数,$0为脚本本身,下面为几个特殊的参数:
参数
功能
$#
传递到脚本的参数个数
$*
以一个单字符串显示所有向脚本传递的参数。如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。即无法用for…in遍历每一个元素
$$
脚本运行的当前进程ID号
$!
后台运行的最后一个进程的ID号
$@
与$*相同,但是使用时加引号,并在引号中返回每个参数。 如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。
$-
显示Shell使用的当前选项,与set命令功能相同。
$?
显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。”
运算符
数字关系运算符
该运算符只支持数字:
运算符
说明
举例
-eq
检测两个数是否相等,相等返回 true。
[ $a -eq $b ] 返回 false。
-ne
检测两个数是否相等,不相等返回 true。
[ $a -ne $b ] 返回 true。
-gt
检测左边的数是否大于右边的,如果是,则返回 true。
[ $a -gt $b ] 返回 false。
-lt
检测左边的数是否小于右边的,如果是,则返回 true。
[ $a -lt $b ] 返回 true。
-ge
检测左边的数是否大于等于右边的,如果是,则返回 true。
[ $a -ge $b ] 返回 false。
-le
检测左边的数是否小于等于右边的,如果是,则返回 true。
[ $a -le $b ] 返回 true。
也可以使用==和!=
注意使用时必须使用方括号括住
eg:
a=1
b=2
if [ $a -eq $b ]
then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b: a 不等于 b"
fi
布尔运算符
! 取非,如[!false]返回true
-o 或运算
-a 与运算
逻辑运行符
&& 逻辑与
|| 逻辑或
字符串比较运算符
运算符
说明
举例
=
检测两个字符串是否相等,相等返回 true。
[ $a = $b ] 返回 false。
!=
检测两个字符串是否相等,不相等返回 true。
[ $a != $b ] 返回 true。
-z
检测字符串长度是否为0,为0返回 true。
[ -z $a ] 返回 false。
-n
检测字符串长度是否为0,不为0返回 true。
[ -n $a ] 返回 true。
str
检测字符串是否为空,不为空返回 true。
[ $a ] 返回 true。
文件测试运算符
常用的几个
[-f file] 检测文件是否为普通文件,既不能是目录也不能是设备
[-d file] 检测是否为目录
[-s file] 检测文件是否为空
[-e file] 检测文件是否存在
[-r file] 检测文件是否可读
[-w file] 检测文件是否可写
[-x file] 检测文件是否可执行
通过expr实现的算术运行符
expr命令可以执行算术表达式并返回结果
如:
a=1
b=2
val=`expr $a + $b`
val=`expr 9 * $b`
注意乘运算需要有转义符,表达示之间必须用空格隔开
输入、输出控制
输出
echo默认输出会换行,可以使用c转义实现不换行
可以通过echo直接回显命令执行结果:
echo `date`
可以使用linux下类型C语言中printf的格式控制输出命令,详见man printf
输入
通过read读取输入:
echo 'Input number'
read a b c d
如果输入为1 2 3 4 5 6,则a,b,c分别为1,2,3,但d是4 5 6
文件重定向
shell的重定向功能非常强大,重定向命令列表如下:
命令
说明
command > file
将标准输出重定向到file。
command < file
将标准输入重定向到file。
command >> file
将输出以追加的方式重定向到 file。
n>file
将文件描述符为n的文件重定向到 file。注意不能有空格,下同
n>>file
将文件描述符为 n 的文件以追加的方式重定向到 file。
n>&m
将输出流n合并到m。可以理解为使用&n来引用名为n的文件描述符,即将文件描述符n重定向到文件描述符m
n
将输入流m合并到n。
<< tag
将开始标记 tag 和结束标记 tag 之间的内容作为输入。
文件描述符0通常是标准输入(stdin),1是标准输出(stdout),2是标准错误输出(stderr),/dev/null为黑洞文件,所有写入到它的内容都会被丢弃,如果不指定数字,则默认为输出为1,输入为0
查看系统文件描述符路径:ls /proc/self/fd/ -l
几个特殊的用法:
n>&-
n>&-表示关闭输出文件描述符n,如1>&-或>&-表示关闭标准输出;类似的n
|&
|&等价于2>&1,即将标准错误合并到标准输出并作为管道的标准输入,输出结果中标准错误将在标准输出的前面,用法为cmd1 |& cmd2,cmd2将以cmd1的输出和错误输出作为输入。
&>/dev/null
&>/dev/null等价于>/dev/null 2>&1,即将标准输出和错误输出重定向到null,什么也不会输出
>/dev/null
等价于1>/dev/null,即将标准输出重定向到null,只会输出标准错误
举例:
1.cat ss.py > cat_test.txt,获取文件ss.py中的内容,并将其重定向到文件cat_test.txt,由于没有指定文件描述符,所以默认为将cat输出到标准输出中的内容重定向到文件。其等价于cat ss.py 1>cat_test.txt。
2.ls tes ss.py 1>out.txt 2>err.txt 表示将标准输出输出到文件out.txt,标准错误输出到文件err.txt
3.ls tes ss.py > oe.txt 2>&1 表示将标准错误输出重定向到标准输出,并将标准输出重定向到文件oe.txt。
4.ls tes ss.py 1>&- 2>&- 关闭标准输出和错误输出,效果等价于以下几个命令ls tes ss.py 1>/dev/null 2>/dev/null,ls tes ss.py 1>/dev/null 2>&1,ls tes ss.py >/dev/null 2>&1, ls tes ss.py &>/dev/null
5.cat > t.txt 将标准输入重定向到t.txt,输入内容之后,通过ctrl+d发送文件结束符停止输入。
6.cat > t.txt < ss.py 从ss.py读取数据,并重定向到t.txt
Here Document是shell用于将delimiter之间的内容重定向到命令的特殊重定向:
command << delimiter
document
delimiter
注意,第二个delimiter必须顶格写,这样其之间的document内容将都会传递给command
修改当前shell session下的所有命令重定向
exec 1>out.txt,执行之后会将当前shell窗口下执行的所有命令的标准输出内容重定向到文件out.txt,而在命令行窗口中不会输出任何内容。可以通过关闭当前shell终端并重新开一个来解决。也可以通过提前将标准输出绑定到一个新的文件描述符,重定向到文件之后,再将其绑回来的方式解决。
如:
exec 6>&1,将6绑定到标准输出1,可以通过ls /proc/self/fd/ -l查看。
exec 1>out.txt,进行一些操作,发现没有输出,实际上结果都在out.txt中
exec 1>&6,将1绑定到文件描述符6,其实就是最初是标准输出
exec 6>&- 现关闭文件描述符6即可
控制流
if
一个完整的if控制流程:
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
for..in
for var in item1 item2 ... itemN
do
command1
command2 ... commandN
done
列表也可以是文件名
for()
for((i=1;i<=10;i++));do echo $(expr $i * 4);done
while
while condition
do
command
done
三种死循环:
while :
do
command
done
while true
do
command
done
for (( ; ; ))
until
until condition
do
command
done
条件为真是停止,类似do..while,循环体至少执行一次
case
case 值 in
模式1)
command1
command2
...
commandN ;;
模式2)
command1
command2
...
commandN ;;
esac
每一个格式后面为右括号结束,模式匹配后一直执行,直到;;(与break功能一样)然后执行*)后面的命令,不再匹配 其他选项。
支持break、continue
函数
函数的定义方式为:
func() {
command return val
}
调用直接函数名后跟参数列表即可:
func arg1 arg2
参数性质与上面说的命令行参数一样,也有那几个特殊参数
注意参数超过10个时必须使用{}引用,如${10}
函数返回值通过$?
add() {
return $(($1+$2))
}
调用:
add 3 5
echo $?
Shell多脚本内容共享
当有两个shell脚本文件时,可以通过. filename或source filename来引用另一个文件中的代码,其实就是同一个shell session之中信息是共享的。
其他相关内容
脚本中需要使用到的命令
用法为 cmd args
该命令后面跟的所有内容都做为其参数,它会对后面的内容(即cmd args)进行两次扫描,第一次扫描将其中的变量替换为实际值,所以如果args中含有需要在bash中执行的以$开头的内容,应该加上转换符(),以避免被替换,例如如果cmd为awk,而awk是通过$来进行参数过滤的。第二次扫描时将后面的所有内容当作同一个命令组合来执行,相当于你直接在命令行中执行该命令。例如脚本如下:
a=5
cmd="echo $a | cat > tcat.txt"
$cmd
如果对cmd的执行不通过调用,而是直接$cmd,输出结果为5 | cat > tcat.txt。这显然不是我们想要的,shell将echo后面的所有内容当成了echo参数来执行。这种情况就需要使用。
exec
与source功能有点相反,source命令会在当前shell进程的上下文件环境中执行各命令。而exec虽然也不会创建新的进程,但会清除当前shell进程的上下文件内容,并执行相应命令。当然它也可以用来对文件描述符进行操作,上文已经提及。
declare(typeset)
内建命令declare与typeset功能类似,用于为变量指定类型,因为默认所有变量都会被当成字符串,当然数组除外。
用法:
declare [-aAfFgilnrtux] [-p] [name[=value] ...]
typeset [-aAfFgilnrtux] [-p] [name[=value] ...]
例如:
$ a=1
$ b=2
$ echo $a+$b
1+2
$ declare -i a=1
$ declare -i b=2
$ declare -i c
$ c=$a+$b
$ echo $c
3
local
用于在shell函数内部声明该变量为局部变量,只对当前函数或其子进程有效。用法:
local [option] [name[=value] ...]
option为declare可以接受的选项。
一不小心这里就会有一个很大的坑,因为默认情况下shell中的变量作用域是全局的,所以你一个for用了i,然后又在另一个函数调用中用了i,那这个i就会被前面的i覆盖,所以尽量在函数中有类似情况的地方用local
用例
输出系统中的所有磁盘
disks=`fdisk -l | grep 'Disk /dev/' | grep -oP '/dev/.{3}'`
for disk in $disks
do
echo '**'
echo $disk
echo '**'
done