最近准备花点时间学习下shell脚本语言,一是考虑到对以后找工作有帮助,因为很多上班了同学还有去面试的同学都说有的公司还是比较看重写shell脚本这个技能的,二是自己的工作需要,可能要写一些简单的脚本,虽然刚学linux时看《鸟哥的私房菜》时学过点皮毛,但现在已经忘的差不多了,刚好借这个机会系统学习一下。先将基础知识复习了一下,为进一步深入学习做准备。
Shell脚本第一行的#!
当一个文件中开头的两个字符是#!时,内核会扫描该行其余的部分,看是否存在可用来执行程序的解释器的完整路径(中间如果出现任何空白符号都会略过),此外内核还会扫描是否有一个选项要传递给解释器,内核会以被指定的选项来引用解释器。
如shell脚本中: #!/bin/csh –f
Shell识别三种基本命令:内建命令,shell函数以及外部命令
1. 内建命令即为shell本身所执行的命令,如cd用来改变目录,read将来自用户或文件的输入数据传给shell变量。
2. Shell函数是功能健全的一系列程序代码,以shell语言写成,它们可以像命令一样引用。
3. 外部命令是由shell的副本即新的进程做执行的命令,其基本过程为:
a. 建立一个新的进程,此进程为shell的一个副本。
b. 在新进程里,在PATH指定的命令中,寻找特定的命令,如果命令以/开头时(说明制定了绝对路径名),将略过查找步骤。
c. 在新的进程里,以所找到的新程序取代执行中的shell程序并执行。
d. 程序完成后,最初的shell会从终端读取下一条命令,或执行脚本里的下一条命令。
shell中的变量
变量就是为某个信息片段所取的名字,变量的赋值方式为:先学变量名称,紧接着=字符,最后是新值,中间完全没有任何空格。通过在变量名前加$可解引用变量,当赋值的内容包含空格时,应加上引号。
first=jack middle=doson last =junior 单行可进行多次赋值
fullname=”jack doson junior” 值中包含空格时使用引号
oldname=$fullname 此时赋值不需要加引号
使用echo,printf输出信息
echo的任务是产生输出,可用来提示用户,或是用来产生数据提供进一步处理,如提示用户输入等,echo会在输出结束后提供换行符。
printf命令模仿C程序库里的printf()库函数,可用于格式化输出信息。
基本的IO重定向
<: 改变标准输入:
program < file 可将program的标准输入修改为file;
>: 改变标准输出
program > file 可将program的标准输出修改为file;
>>: 附加到文件
program > file 可将program的标准输出附加到file的结尾处;
|: 建立管道
program1 | program2 可将program1的标准输出改为program2的标准输入;
特殊的文件: /dev/null
传送到/dev/null文件的数据会被系统丢掉,即当程序将数据写到此文件时,会认为它已成功完成写入数据的操作,但实际上什么事也没有做;从/dev/null读取数据则会立即返回文件结束符。
如当需要的是命令的退出状态,而非其输出,此功能会非常有用,例如测试某个文件是否包含某个模式(pattern):
if grep pattern myfile > /dev/null
then
… 找到匹配模式
else
… 找不到匹配模式
fi
基本命令查找
shell会沿着查找路径$PATH寻找基本命令,$PATH是一个以冒号分隔的目录列表,列表中的目录为指定的命令查找位置,所找到的命令可能是编译后的可执行文件,也可能是shell脚本。
$PATH里的空项目表示当前目录,空项目位于路径中间时,可用两个连续的冒号来表示,如果将冒号直接置于前端或尾端,可以分别表示最先或最后查找当前目录。
PATH=:/bin:/usr/bin 先查找当前目录
PATH=/bin:/usr/bin: 最后查找当前目录
PATH=/bin::/usr/bin 当前目录居中
当然在PATH中加入当前目录最好的方法还是直接使用点号,这样更具可读性。
shell脚本的参数
$0: shell脚本的名称
$#: 参数的总个数
$*: 代表所有的参数,扩展为”$0 $1 … $n”
$@: 扩展为”$0” “$1” … “$n”
$1~n: 代表第n个参数,当超过9时,应使用大括号,如${10}代表第10个参数。
简单的执行跟踪
为了能迅速定位错误位置,就是把执行跟踪功能打开,这会使得shell显示每个被执行到的命令,并在前面加上“+ ”(加号+空格)。
如:
$ sh –x nusers 通过-x执行跟踪功能
//也可以在脚本中通过set –x/set +x 动态开启或关闭该功能。
+ who 被跟踪的命令
+ wc –l
7 实际的输出
查找与替换
主要内容包括unix shell的正则表达式,以及模式匹配工具grep,awk,流编辑器sed,剪切工具cut,连接工具join的基本使用。
export,readonly,unset,env管理变量
readonly可以使变量成为只读模式,从而禁止改变变量的值,可用于在shell中创建常量。
export用于修改或打印环境变量。环境是一个名称与值的简单列表,可供所有执行中的程序使用。新的进程会从其父进程继承环境,export则可以将新变量添加到环境中。
unset从当前shell删除变量与函数。
env为在命令行执行程序的环境提供更细致的控制。
如: readonly hours_per_day=24 seconds_per_hour=3600 days_per_week=7
export PATH=$PATH:/usr/local/bin
export –p 显示当前的环境
unset hours_per_day 删除hours_per_day变量
env –i name=value… command arguments
忽略继承的环境,只使用命令行的环境执行command
参数展开
参数展开是shell提供变量值在程序中使用的过程。
默认情况下,未定义的变量会展开为null(空的)字符串。
如: rm –rf /$program 如果program未被定义,则会引发灾难。
展开运算符
1. 替换运算法
${varname:-word} 如果varname存在且非null,则返回其值,否则返回word;可用于当变量未定义则返回默认值的情况。
${varname:=word} 如果varname存在其非null,则返回它的值,否则设置varname为word,并返回其值;可用于当变量未设置则设置其为默认值的情况。
${varname:?message} 如果varname存在且非null,则返回它的值,否则显示varname:message,并退出当前的命令或脚本。可用于捕捉变量未设置引发的错误,message为提示消息。
${varname:+word} 如果varname存在且非null,则返回word,否则返回null;可用于测试变量的存在性。
2. 模式匹配运算符
令path=/home/tolstoy/mem/long.file.name
${variable#pattern} 如果模式匹配与变量的开头处,则删除匹配的最短部分,并返回剩下的部分。如${path#/*/}返回tolstoy/mem/long.file.name。
${variable##pattern}如果模式匹配与变量的开头处,则删除匹配的最长部分,并返回剩下的部分。如${path##/*/}返回long.file.name。
${variable%pattern} 如果模式匹配于变量的结尾处,则伤处匹配的最短部分,并返回剩下的部分。如${path%.*}返回tolstoy/mem/long.file。
${variable%%pattern} 如果模式匹配于变量的结尾处,则伤处匹配的最短部分,并返回剩下的部分。如${path%%.*}返回tolstoy/mem/long。
特殊变量
# 目前进程的参数个数;
@ 传递给当前进程的命令行参数,等同于”$1” “$2” …
* 传递给当前进程的命令行参数,等同于”$1 $2 …”
? 前一进程的退出状态
$ shell进程的进程编号
0 shell程序的名称
! 最近一个后台命令的进程编号
HOME 当前用户根目录
IFS 内部的字段分隔器,一般设为空格,制表符或换行符
LANG 当前locale 的默认名称
PATH 命令的查找路径
PPID 父进程的进程编号
PWD 当前工作目录
PS1 主要的命令提示符字符串,默认为$
PS2 行继续的提示符,默认为>
PS4 以set –x设置的执行跟踪的提示字符串
算术展开
shell的算术运算符与C语言里的差不多,优先级与结合性也相同。
shell将置于$((…))中的内容解释为数学运算。
如: echo $((3 && 4)) 输出1
echo $((3 * 100)) 输出300
&&,||均为快捷运算符,当测试表达式的值确定后就停止执行。
退出状态
每一条命令,不管是内置的,shell函数,还是外部的,当它退出时,都会返回一个小的整数值给引用它的程序,该值即代表程序的退出状态。通常0表示成功,即程序执行完成且未遭遇任何问题。
POSIX的结束状态
0 命令成功地退出
1-125 命令不成功第退出。特定的退出值的含义,是由单个单独的命令定义的。
126 命令找到了,但文件无法执行
127 命令找不到
>128 命令因收到信号而死亡
exit可用于从shell脚本返回一个退出状态给脚本的调用者
if-elif-else-fi语句用于判断
if 测试条件1
then 执行动作1 #可以是多条语句
elif 测试条件2
then 执行动作2 #可以是多条语句
……
elif 测试条件n
then 执行动作n #可以是多条语句
else
执行动作 #可以是多条语句
fi
test命令
test expreesion 或者 [ expression ](两边有空格)
test string 或 [ string ] : string不为null
test –b file 或 [ -b file ]: file是块设备文件
详细的说明参见man test
case语句
case $varname in
pattern1) 与pattern1匹配
… 执行命令
;;
pattern2) 与pattern2匹配
… 执行命令
;;
*) 默认情况
.. 执行命令
;;
esac
for循环
for循环用于重复整个对象列表,依次执行每一个独立对象的循环内容。
如:for var in a.xml b.xml c.xml
do echo $var
done
该循环输出每一个xml文件的名字,如果没有指定列表的内容,则shell循环遍历整个命令行参数。
如:for var in
do
****多每一个参数进行的处理
done
while, until循环,break,continue
while conditon until conditon
do do
statements statements
done done
while循环:只要condition是成功退出或为真,while会继续循环;
until循环:只要conditon未成功结束或为假,until继续循环。
break,continue:类比C语言中的用法,break与continue命令都接受可选的数值参数,可分别用来指出要中断(break)或是继续(continue)多少个被包含的循环。如:
while condition1 外部循环
do
while condition2 内部循环
do
break 2 跳出外部循环
done
done
…… 中断之后继续执行