编写 shell脚本
来源:互联网 发布:软件开发与软件研发 编辑:程序博客网 时间:2024/06/05 02:56
之前两周,参考<快乐的linux命令行> 学习了基本的shell命令,虽然好多细节没有记得很清楚。但大致操作了下。
下面,将学习 如何编写shell脚本
<第26章 编写第一个shell脚本>
我们通过前面章节,大概学习了shell命令行的使用. 他们一般单独的出现在 Terminal的命令行中,完成某种单一的功能。
shell脚本是 把这些整合在一起,完成一个 较大的 函数功能。
一个shell脚本,赢具备 基础的三点特征:
(1) #! /bin/bash shebang打头
(2) 具有可执行权限,一般是 755 或者700权限
(3) 最好把脚本 放在一个 路径下, 并且加入环境PATRH内。 这样更加便捷。
脚本位置说明: 脚本的存放路径置于 PATH 内,在命令行中调用脚本时,shell会自动搜索各PATH的 可执行文件,给出补全符.
这样不管在什么路径下,都能直接调用某一脚本,不用费心的输入脚本所在的路径。
<第27章 启动一个项目>
其实呢,整个shell脚本的编写,有点像C程序的编写. 整个结构也是相似,本质上是编程思想。
1.定义 变量
C程序中最常用的就是设置变量:如 a=2;b=1;b=b+1. shell脚本为了便捷,也会如此处理.如 set printlog= " this step is rigth"
需要注意的shell 定义变量的 语法: =号前后都不能出现空格. (原因; 变量=表达式. 变量名称必须是字母下划线数字不能有空格符,所以=前面不能有空格
后面可以有空格,但必须用 双引号 ,因为shell遇到空格认为,变量字符串结束了。
默认 大写字母表示 常量; 小写字母表示 变量。
2. 调用变量
定义过的变量,可以使用$符号置换, 常见的使用方法为 $a $b
其实这是 省略写法, 真正的变量调用是: ${a} 其中花括号作用是 : 告诉命令解析器,变量名字的边界位置。 推荐给所有变量加上{},这是一个好的习惯
3. 定义函数
有两种方法定义
function name { name () { 即 关键词 function可以省略掉
cmd cmd
[ return ] [ return ]
} }
return可以加,也可以不加;不加时,会吧最后一个cmd的结果返回出来. 如果加,必须是0-255之间的一个数字.通常用1和0.
returen返回值 保存在 $? 这个变量中
调用函数: name 【参数】.... 会自动把参数命名为 $1 $2 $3 ..... 如果参数数目不够,就相当于 $2 $3..为空
4. 函数内部变量;
在函数内部,我们希望定义一些 可读性较好的变量名. 这些值仅在函数内部使用即可. 为了避免和外面变量混淆.起名字将会非常困难.
我们只需要在 函数内部定义下: local a;
a=hhh; 就可以在该函数内,安全的使用变量a
<第 二十八章 if 结构>
在上面一小节中,我们知道了变量的定义方式,包括局部变量、全局变量。 也了解了一些 shell函数的定义方式。
我们知道 shell函数会在最后 返回一个值。 (但我们还不是很清楚的知道这个返回值 究竟是什么,代表什么意义)
1. 当shell命令执行后,不论对错都会有一个返回值,写入 "? "这个全局变量中。
特别注意: 0 代表 命令执行正确;其他值表示命令执行失败. 而且这个返回值,是0-255之间的。
2. shell内部有两个 命令: true 和 false. true一定能执行成功,使得 $?=0 .
false一定执行失败, 使得 $? =1
需要特别提醒的是: shell 中, 0代表Ture . 非零代表false. 这一点于很多语言都不一样。
3. 再看 if 语句.
shell中的if结构: if commands 1 ; then
operation1...
【else if cmomands2 ; then
operation2 ...
】
【else operation3 ... 】
fi
if 先判断 commands1 命令系类的 最后一个命令的执行结果. 如果正确执行了,或者返回值是 0.(代表True) . 就进行operation1.
否则再去判断 command2......
4. 我们常用的 if 条件
(1) if test expression 的意思是 test 是一个命令, 判断expression表达式的真伪. 也就是说 command1 = test expression
(2) 当然了,这样书写,对于shell脚本可读性不太好,它的等效写法是 if [ expression ] 注意[] 前后要有空格。
本质上就是 test expression = 【 expression 】
常用的表达式 expression 主要有一下:
-e file :存在 (注意file是 路径+文件名)
-f file : 存在且是一个普通文件。
-w file : 某文件可写
-x file : 某文件可以被执行
-r file : 某文件可读
-z string : 字符串长度为0
-n string: 字符串长度大于0
string1 == string2 : 字符串1 和字符串2相同
string1 != string2 : 字符串1 和字符串2相同
string1 \> string2 : ascii 排序 string1 排在string2前面
sting1 \< string2 : 排序 string1 排在 string2 后面
integer1 -eq integer2 : 整数1 等于整数2
integer1 -ne integer2 : 整数1 不等于整数2
-lh : less than 小于
-le : less equal 小于等于
-gh : greater than 大于
-ge : greater equal 大于等于
if [ -e file -a -r file ] 表示文件存在 并且可读
if [ $a -eq 2 || $a > 5] 表示数据a等于2 或者大于5时 为真 执行...
if [ ! -e file ] 文件不存在
5. [[ ]] 和[] 的区别
[ ] 是一个命令 [ expression ] 里面的expression 是 命令[ ] 的参数。(等价于 test expression) 。 并且呢, 这个[ ] 命令在 很多shell中都是支持的。
比如 bash、csh、tsh等等。
我们知道shell 运行时, 会对 命令的参数执行 字符预扩展。 所以 expression 里面出现 特殊字符时候,需要注意。 最好前面加上 反斜杠,使其变成正常字符、
[[ ]] 不是一个命令,而仅仅是一个 特殊的书写符号。用作关键字。 并且并不是所有的shell 都支持 这样书写。
bash、ksh支持,而 ash、bsh不支持。 两者完成的功能基本相同、 下面将比较两个的异同点
类别 [ ] [[ ]]
(1) 整数测试 -eq -lt -gt -ge -le -ne 支持 支持
(2) 文件测试 -e -w -r -x 等等... 支持 支持
(3) 字符串测试 == != -z -n < > < > 前面需要加反斜杠转义 支持
(4) 逻辑表达式 -a -o ! && || ! (两者描述 条件1 ...条件2 的语法不一样)
(5) 简单模式匹配. 仅支持 * . 正则,不支持? +等其他 支持
(6) string =~ regep 不支持 特别技能!!!
(7) 算数扩展 如 99+1 不支持 支持 (但是不推荐使用)
6. 上面我们比较了[] 和 [[ ]] 的异同点, 一版而言 [[ ]] 语法不易出错且宜读。推荐使用 [[ ]]
上面第(7) 算数扩展项,我们不推荐使用[[ ]] 。那用什么好?? 后面章节会再做补充
<< 第29章 和键盘进行交互>>
我们在使用 shell脚本时候,都是直接调用就可以了。 但是有一些脚本,需要处理键盘输入数据。 这个时候 键盘和脚本怎么交互呢?
read命令就能完成此任务。
read [ option]... [Varname] : read + 选项 + 参数名
当在shell命令行中输入: read var1 之后. shell命令行会自动的另起一行, 让我们我们输入信息. 我们输入完信息之后,按下回车键。read命令就执行完毕了。如下所示
[caokang@caokang Prj]$ read var1asdasd[caokang@caokang Prj]$
这个命令完成的功能是: 把输入的信息"asdasd" 赋值给 Var1变量。
当输入多个 变量名,如下:
[caokang@caokang Prj]$ read var1 var2 var3a b d d e[caokang@caokang Prj]$ echo $var1 a[caokang@caokang Prj]$ echo $var2b[caokang@caokang Prj]$ echo $var3d d e
read命令的机制,是把 第一个空格前输入信息 赋值给变量1 , 第1~2个空格之间的数据赋值给变量2. 在把最后的数据 全都给变量3.
如果输入信息如下
[caokang@caokang Prj]$ read var1 var2 var3a[caokang@caokang Prj]$这时候var1 =a var2、var3都是空。
同样,当我们 只输入 read,不带有 参数名时候: 这个时候会把 下面一整行的输入信息,存入REPLY变量中
[caokang@caokang Prj]$ readhahahsh[caokang@caokang Prj]$ echo $REPLY hahahsh[caokang@caokang Prj]$
所以为了避免 输入数据信息和 变量名字不一样,最好能加入一些提醒信息: 告诉我们要输入什么信息,输入几个。这个功能就有 -p 选项完成
[caokang@caokang Prj]$ read -p "please enter 3 num :" var1 var2 var3please enter 3 num :1 2 3[caokang@caokang Prj]$ echo $var11[caokang@caokang Prj]$
-p sting 选项完成的功能是, 输入信息栏,前面 打印一些信息(string)
以上 完成的是基本的 键盘交互的变量存储问题. 那么如果我们 脚本执行到此后, 键盘提示我们输入信息,我们忘记了,这时候shell脚本就会卡死在这里,死等。
为了解决这个问题, 有了 -t num这个选项. 表示 超过 num秒, read命令执行无效.
[caokang@caokang Prj]$ read -t 5 var1[caokang@caokang Prj]$
-s选项: 有时候啊,我们输入信息, 但是不想在 shell命令行中显示出来。比如 输入 root密码。 -s选项就是为了实现这个功能。
[caokang@caokang Prj]$ read -s var1[caokang@caokang Prj]$ echo $var1123456[caokang@caokang Prj]$
从文件中读入数据: 除了上文介绍的从键盘输入信息, read也支持从文件中读取内容. 但是感觉这个功能有些鸡肋...
<< 第三十章 流程控制 >>
在这一小节中, 我们将要介绍 while 循环语句的用法.
while 条件 ; do
操作; [ break; continue]
done
其中 break代表,挑出while循环; continue 跳过此次循环,进行下一次while(重新判断)
条件 一般跟 if 一样,使用 test命令,或者是[] 、 [[ ]] .
跟c语言里面类似,如下 while(i >=0) { i--;} 我们知道 操作里面需要更新 条件i的值,才不至于整个程序卡死。
那么我们经常会用 数字运算, 来表示循环次数.
那么shell中进行数学运算的途径有哪些呢?
(1) let命令 + - * / % ++ -- += -=
比如 let a++ let b=a-2 (注意变量a、b前面不能加$)
(2) $[ ] 命令,
i=$[ a -1] i=$[$a-$b] 也就是说 a、b变量前面的$可有可无。
(3) $(( express )) 模式
只要里面的表达式express 符号 c语言规则,就能使用. 比如
$(( a +b ))
经典应用 从文件a.v 中读取每一行内容,并打印出;
while read linetxt ; do
echo "$linetxt"
done < a.v
这里呢 , < a.v表示重定向,a.v到键盘输入,就是相当于 read 文件a.v read 读文件,最后一行之后,会返回False值。 即while (false)。
<第 三十二章 流程控制 case语句>
还是借鉴c语言构架,本小节介绍 case语句的使用.
对比C语言,
case(xxx)
8‘h01 : 。。。。;break;
8'h02: .。。。。; break
................
default : 。。。。; break
endcase
同样的 shell中 case语句结构大概相同。
case ( 变量 ) in
pattern1 ) commands ;;
pattern2 ) commands ;;
。。。。。
* ) commands ;;
esac
其中 pattern 注意 ,只支持字符串、通配符。 如 a 字符a ?一个字符 * 任意字符 [a-z] 小写字母 这种,支持POSIX表达。 不支持正则表达式。
如果想用 if [[ sting =~ regep ]]
应该注意到, bash本身是不支持 正则表达式的,只有少数命令的 参数上可以使用,如 sed \grep\ if [[ ]] . 这些支持Regep的地方都会特别的标注出来.
也可是使用: pattern1 | pattern 2 ) 表明是 或条件,满足其1即可
<< 第 三十三章 位置参数 >>
习惯上,我们在shell命令行中,调用shell脚本时候. 一般都是直接调用就完事了.
比如: 创造了一个脚本. 用于把 log文件中的Error信息行抓取下来, 为了能兼容其他. 我们最好做成这样的设计:
[caokang@ Prj] $ pick_err dc.log
[caokang@ Prj] $ pick_err verdi.log 在后面直接尾缀上,需要处理的文件.
是不是想起来 公司里面跑 综合\PT的脚本,都是 sync_dc BK5121 umc11f SPEF 尾缀好几个参数。
本章所讲述的内容就是 ,如何把这些参数 传送到shell脚本里面来。
本质上,通过变量传递的方法进行传输: 把从shell命令行中输入的众多参数,依次存储在 特定的变量中,然后shell脚本可以直接使用该变量。
$0 : 脚本名称(含路径) 即在shell命令行中,输入的脚本名称,
$1 : 第一个参数(如BK5121)
$2:第二个参数(如 umc11f)
$3---n 依次为后面的内容。如果没有,该变量就是空。 特别说明,如果 n大于10了,使用${10} . 前文中介绍过。大括号用于告知shell解析器,变量名的边界。无特殊意义
$# : 变量个数,即n值。(不包括$0)
$* : 直接使用$*的时候, $* = $1 $2 $3 ......$n 用空格链接起来
$@ : 直接使用$*的时候, $@ = $1 $2 $3 ......$n 用空格链接起来
"$* " 用双引号 引用起来后。 变成一个统一的变量 “ $0 $1 $2 ....$n”
"$@ " 用双引号 引用起来后。 变成一个统一的变量 “ $0” “ $1” “ $2 ” .... “$n”
当要把参数列成表格使用时候, 推荐使用 ”$@“
2. 在这里学习下: basename dirname这两个命令。 它们是为了获取某一文件的 路径、或者仅仅文件名的 命令。
dirname很好使用: 获取其路径参数
[caokang@caokang Prj]$ dirname /home/caokang/Prj/stderr.txt /home/caokang/Prj[caokang@caokang Prj]$
注意: 如果特别 指明了 绝对路径.则获得. 否则获得的是相对路径. 如下" . "
[caokang@caokang Prj]$ dirname stderr.txt.ssd.ae .[caokang@caokang Prj]$
basename 用于去除后缀. basename -flie ssffix
去除一个文件的 后缀。 如果没有指定 尾缀是什么东西,就去除空。
如果指定了尾缀,则去除文件名中的 该尾缀。
[caokang@caokang Prj]$ basename /home/caokang/Prj/stderr.txt.ssd.ae .aestderr.txt.ssd[caokang@caokang Prj]$上面的例子, 去除的尾缀是" .ae" 下面是指定空 尾缀的情况
[caokang@caokang Prj]$ basename /home/caokang/Prj/stderr.txt.ssd,ae stderr.txt.ssd,ae[caokang@caokang Prj]$
3. 常用的 提示信息;
上面我们说了参数如何传递进入脚本。 那么在命令行中输入时候,我们会经常性的漏掉、或者次序颠倒。 这一小节,就介绍如何做提示信息。
<< 第三十四章 for循环>>
依旧跟C语言类似。 前面 if判断、while循环、case选项。还有一个for循环没有介绍。
shell中的for循环有两种表述形式:
(1) 传统描述
for var in 1 2 3 4 5 ...N ; do
cmds
done
即 var分别等于 1 2 3 4 5 ...N时候 ,执行 cmds命令。 1 2 3 4 5 ...N 相当于是个参数集合,是一个列表 .用空格隔开就会被认为是一个参数。
这个命令真正强大的地方在于, 使用别的命令得到 列表, 用于 for循环.
比如 : for var in *.txt ;do
echo "$var"
done
shell命令行遇到for列表处,进行元字符替换,得到一个文件列表。
或者是使用 命令结果用作列表.
比如
for var in $(ls) ; do
echo $var
done
shell命令行,会先执行ls命令,得到的结果充当 列表,供for循环使用
在这里不得不提下: shell中如何获得 某一命令的结果。
(1) `cmd`
(2) $(cmd)
C语言类型的 for 命令表达:
for(( expr1 ; expr2 ;expr3 )) ; do
cmds
done
比如:
for(( i=0 ; i<5; i=i+1 )) ; do
echo $i
done
<< 第三十五章 字符串和数字>>
之前讲述的很多命令,更多的是以文件为操作对象的. 本小节,谈论下 字符串和数字.
通常不论字符串还是数字,我们都会把它放到一个变量中去. 当需要使用的时候,去调用变量即可. 这就涉及到了 变量置换.
(1) 默认是 $a 参数 $a=${a} . 我们之前讲过, 花括号的用处是告诉shell命令解析器 变量名字的范围。
${varname:-"repace value“} :当varname没有定义或者 定义为空时候, 该命令返回 replace value
${varname:="repace value“} :当varname没有定义或者 定义为空时候, 该命令返回 replace value . 于此同时varname会被赋值 varname= repalce value.
[caokang@caokang Prj]$ foo=[caokang@caokang Prj]$ echo ${foo:-"hashh"}hashh[caokang@caokang Prj]$ echo $foo [caokang@caokang Prj]$
这里, foo定义成空。 ${foo:-"hashh"}返回值就是 hashh。 但此时 foo变量仍是空
[caokang@caokang Prj]$ foo=[caokang@caokang Prj]$ echo ${foo:="hashh"}hashh[caokang@caokang Prj]$ echo $foo hashh[caokang@caokang Prj]$这里,foo依旧定义成空, := 返回值是 hashh,同时foo变量已经被赋值了。
${varname:?"Note var is null or not defined “} :当varname没有定义或者 定义为空时候, 命令执行情况是1.(表示运行错误)。 返回值是 Note: var is...........
[caokang@caokang Prj]$ foo=[caokang@caokang Prj]$ echo ${foo:?"NOte, foo is null or not defined"}bash: foo: NOte, foo is null or not defined[caokang@caokang Prj]$ echo $foo [caokang@caokang Prj]$
${varname:-"repace value“} :当varname没有定义或者 定义为空时候, 该命令返回仍未空
但是当varname定义时, 此时会返回 repace value. 仅仅只一次. varname的值依旧一样
[caokang@caokang Prj]$ foo=jjjj[caokang@caokang Prj]$ echo ${foo:+"repace once"}repace once[caokang@caokang Prj]$ echo $foo jjjj[caokang@caokang Prj]$
通过上面这四种情况,可以在必要的时候引用,避免来回的修改变量值。
(2) 返回变量名的参数展开
${#varname} 返回该变量值,字符串的长度。
${#varname:offset:length} 返回varname值 从offset开始,length长度个 字符。
${#varname: offset} 当不指定length时,默认是到字符串结尾。
${varname#pattern} :把varname值的 字符串,符合正则表达式的最短匹配项,删除.
${varname##pattern} :把varname值的 字符串,符合正则表达式的最长匹配项,删除.
${varname%pattern} :把varname值的 字符串,符合正则表达式的最短匹配项,删除. (从尾部开始匹配)
${varname%%pattern} :把varname值的 字符串,符合正则表达式的最长匹配项,删除. (从尾部开始匹配)
${varname/pattern/string} :把varname值的 字符串,符合正则表达式的第一个匹配项部分,替换成为 string
${varname//pattern/string} :把varname值的 字符串,符合正则表达式的全部匹配项部分,替换成为 string
[caokang@caokang Prj]$ echo $test word word1 test word2[caokang@caokang Prj]$ echo ${test/word/temp}temp word1 test word2[caokang@caokang Prj]$ echo ${test//word/temp}temp temp1 test temp2[caokang@caokang Prj]$
本质上 这些命令,都可以使用sed命令进行处理. 但提前了解一点也好.
(3) 变量内容的大小写转换。
${var,,} 把var内容去不变成 小写字母
${var,} 把var内容的第一个 字符变成 小写。
${var^^} 把var内容去不变成大写字母
${var^} 把var内容的第一个 字符变成 大写。 (bash不支持??)
<< 第三十六章 数组 >>
数组是一种很重要的 数据结构.
(1) 定义数组
a=(a0 a1 a2 a3 a4 a5) 定义一个长度为6的数组
a=() 定义了一个空数组
(2) 赋值数组
单个赋值 a[0] = "hhh"
注意: a=
代表是元素a[0]为null。 而不是整个数组a 赋null
(3)数组元素操作
整个数组的整体操作
跟之前讲述的 $* $@ "$*" "$@"一样.
${arry[*] = a[0] a[1] 。。。。a[n]
${arry[@] = a[0] a[1] 。。。。a[n]
"${arry[*]" =" a[0] a[1] 。。。。a[n]"
"${arry[@]" =" a[0] " " a[1]" 。。。。"a[n]"
${#a[@] 或者 ${#a[*] 获得真个数组 元素的个数
在数组末尾添加元素: a +=(a b c) 在数组a后面再追加三个元素。 a b c
删除数组: unset a 删除整个数组
unset 'a[2]' 删除元素a[2]
关联数组: 与普通数组的区别在于, 数组下标 可以用字符串表示。
declare -A colors 定义了一个colors的关联数组
colors["red"]="ff0000"
colors["blue"]="00ff00"
colors["yellow"]="0000ff"
结语: 在学习 <<快乐的linux命令行>> 这本书, 从基础讲起。系统了介绍了shell命令、打下基础。 对我这种初学者帮助很大.
给本书的作者点赞。
- shell脚本编写技巧
- 编写shell脚本
- 编写shell脚本
- 如何编写shell 脚本
- shell脚本的编写
- shell脚本编写技巧
- shell脚本编写技巧
- shell脚本编写
- 如何编写shell脚本
- Shell脚本编写
- shell脚本编写技巧
- Shell脚本编写教程
- 编写Shell脚本
- Linux Shell脚本编写
- shell脚本的编写
- shell脚本编写
- shell脚本编写汇集
- 编写Shell管理脚本
- 北大 AI 公开课第6讲:王俊——DNA是生命数字化的过程,AI改变生命科学
- 深入解析MySQL分区(Partition)功能
- plus-one
- Cocos2d-x v3.X的颜色混合BlendFunc使用详解
- Android getLocationInWindow()返回空值
- 编写 shell脚本
- 修改maven的镜像仓库,提高下载jar包的速度
- GDOI2017模拟Round4总结
- python——模块
- 怎么做bug预防?
- REST风格概述
- MySQL数据库
- python-opencv旋转图像,保持图像不被裁减
- ACM 水仙花数