shell:是一种命令解释器还是一种编程语言(shell脚本,shell编程),擅长处理文本类型的数据,所以Shell脚本在管理Linux系统中发挥了巨大作用
备份文件、安装软件、下载数据之类的事情,学着使用sh,bash会是一个好主意.
1 2 3 4 5 6
| #!/bin/bash IFS=$'\n' IFS=$'\n:;"'
|
脚本文件没有可执行权限时
脚本有可执行权限
1 2 3 4 5 6 7 8 9
| ./test.sh source 或.
|
重定向输入和输出:将某个命令的输出重定向到另一个位置(文件,或者命令)
命令行的数据流定义:STDIN(0),STDOUT(1),STDERR(2),用来管理SHELL中的信息流的分流.
重定向输出:将命令的输出发到一个文件中
1 2 3 4 5
| STDOUT: >(覆盖) >>(追加) 重定向到文件 STDERR: 2> STDOUT STDERR: 2>&1 ehco "alias ll='ls -alF'" >> test.txt date > test.txt
|
重定向输入:将文件的内容重定向到命令中
1 2 3
| grep femn < /etc/passwd grep femn < /etc/passwd echo `grep femn < /etc/passwd`
|
管道命令:将一个集合的输出做会一个命令的输入 STDOUT,STDERR | STDIN
生成已安装包的列表,经过管理排序,再经过管理传给More命令显示
Shell基本类型
赋值与引用:
脚本编程中的一个主要构件 反引号 ( `` ) 它允许你将shell命令的输出赋给变量
1 2 3 4 5
| testing=`date +%y%m%d` $HOME ${HOME} echo echo -e "OK! \n" /$15
|
变量类型只能为Int,str,数组,而且等于两边不能有空格
数字
在将一个数学运算结果赋值给一个变量时,你可以用美元符和方括号将数字表达式圈起来
注意只支持整数运算
1 2 3
| var=1 var1=$[2 * 5] var1=$(($var*5))
|
字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| var=femn greeting="hello, ${var} !" var="my name" echo ${#var} echo ${var:1:4} if [-z str 为0 ] if [-n str 非0] var=http://www.aaa.com/123.htm 1. echo ${var#*//} 2. echo ${var##*/} 3. %号截取,删除右边字符,保留左边字符 echo ${var%/*} 4. %% 号截取,删除右边字符,保留左边字符 echo ${var%%/*} 5. 从左边第几个字符开始,及字符的个数 echo ${var:0:5} 6. 从左边第几个字符开始,一直到结束 echo ${var:7} 7. 从右边第几个字符开始,及字符的个数 echo ${var:0-7:3} 8. 从右边第几个字符开始,一直到结束 echo ${var:0-7}
|
数组
1 2 3 4
| list=(1 2 3 4) echo ${list[0]} echo ${list[@]} echo ${#list[@]}
|
shell语法
结构化命令允许你改变程序执行的顺序,在某些条件下执行一些命令而在其它条件下跳过另一些命令.
if-then语句,只能测试跟命令的退出状态码有关的条件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 1.if command:then commands fi if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi 2.if command1 then commands else commands fi if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; \ else echo 'false'; fi 3.if command1 then commands elif command2 then commands fi
|
case命令,替代elif语句来不断检查相同变量值.case命令提供了一个更清晰的方法来为变量每个可以的值指定不同的选项.
1 2 3 4 5 6 7 8
| case $USER in femn | pattern2 ) commands1;; parrent3) commands2;; *) commands;; esac
|
test命令会将所有的标点和大小写也考虑在内
在其它编程语言中,if语句之后的对象是一个等式来测试是 true or false .bash shell并不是这样工作的.
当command1命令退出状态码是0,位于then部分的命令就会被执行.如果是非0则执行else命令块.
test命令提供了在if-then语句中测试不同条件的途径.
test命令格式:test condition(要测试的一系列参数和值)
1 2 3 4 5 6 7
| if test ${num1} -eq ${num2} if [ ${num1} -eq ${num2} ] [ condition1 ] && [ condition2 ] ||
|
test可以判断3类条件:1.数值比较,2.字符串比较,3.文件比较
数值比较:
1 2 3 4 5 6
| -gt: 大于(-g) -lt: 小于(-l) -eq: 等于(-e) -ne: 不等于 -ge: 大于等于 -le: 小于等于
|
bash shell能处理的数仅有整数.使用bash计算器时,你可以让shell将浮点值作为字符串值存储进一个变量,如果你只是要通过echo语句来显示这个结果,那它能很好地工作,便它无法在基于数字的函数中工作.
字符串比较:
字符串顺序:要测试一个字符串要比另一个字符串大就开始变得烦琐了,大于小于符号必须转义,否则shell会把它们当做重写向符号而把字符串值当做文件名.而且大于小于顺序和sort命令所采用的不同.test命令中大写字母会被当成小于小写字母.
if [Test \< test]
文件比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if [ ./new.sh -nt ./old.sh ] if [ ./old.sh -ot ./new.sh ] if [ -e $HOME ] if [ -d $HOME ] if [ -f $HOME/.vimrc ] if [ ! -f $HOME/.vimrc ] if [ -r $HOME ] if [ -w $HOME/.vimrc ] if [ -x $HOME/.vimrc ] if [ -O $HOME/.vimrc ] if [ -s $HOME/.vimrc ]
|
if-then的高级特征:
双圆括号命令允许你将高级数学表达式放入比较中
test命令只允许在比较中进行简单的算术操作.双圆括号命令提供了更多的为用过其它编程语言的程序员所熟悉的数学符号.你不需要将圆括号中表达式里的大于号转义,这是双圆括号命令提供的另一个高级特性.
用于高级字符串处理功能的双方括号. [[ expression ]]
1 2 3 4
| if (( $var1 ** 2 > 90 )) || [[ $USER == f* ]]
|
循环中定义的test命令和if-then语句中定义的是一样的格式.
你需要重复一组命令直到达到某个特定条件.for, while, until
for循环假定每个值都是用空格分割的
1 2 3 4 5 6 7 8 9
| for var in list do commands done for file in `ls /etc` for loop in 1 2 3 4 5 for var in item1 item2 ... itemN; do command1; command2… done; for test in I don\'t know if "this'll " work
|
用通配符读取目录:你可以你for命令来自动遍历满是文件的目录.进行操作时,你必须在文件名或路径名使用通配符.它会强制shell命令使用文件扩展匹配(file globbing):它是生成匹配指定的通配符的文件名或路径外的过程.
1 2 3 4 5 6 7 8 9 10 11 12 13
| for file in $HOME/* do if [-d "$file"] then echo "$file is a directory" elif [-f "$file"] then echo "$file is a file" else echo "$file is doesn't exist" fi done
|
C语言的for命令:通常会定义一个变量,然后这个变量会在每次迭代时自动改变.程序员会将这个变量当作计数器,并在每次迭代中让计数器增一或减一.
这是bash中C语言风格的for循环的基本格式:for((variable assignment;condition;iteration process))
1 2 3 4 5 6 7 8 9 10 11
| for (i=0;i<10;i++) { print("the next number is %d\n",i) } for ((a=1,b=10;a<10;a++,b--)) do echo "$a - $b" done
|
while
while命令在某种意义上是if-then语句和for循环的混杂体.它会在每个迭代的一开始测试test命令.只有测试条件成立,while命令才会继续遍历执行定义好的命令.
1 2 3 4 5 6 7 8 9 10 11 12
| while test command do other command done #!/bin/bash int=1 while(( $int<=5 )) do echo $int let "int++" done ))
|
break终止执行后面的所有循环
continue命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环
until命令和while命令工作的方式完全相反
测试条件中用到的变量必须被修改,否则会进入一个无限循环.
嵌套循环nested loop:可以在循环内使用任何类型的命令,自然也包括其它循环命令.在使用nested loop时,你是在迭代中使用迭代,命令运行的次数是乘积关系.内部循环会有外部循环的每次迭代中遍历一遍它所有的值.
以两个for命令写个nested loop
shell变量
bash shell提供了不同的方法从用户处获得数据,包括命令行参数,命令行选项以及直接从键盘读取输入的能力.
命令行参数(添加在命令后的数据值)
$?专属变量来保存上个执行命令的退出状态码(0:命令成功结束,126:没有执行权限,1:无效参数等通用未知错误)
$1-$9 第一到第九个命令行参数
${10} # 第十个命令行参数的读取方式
当传给$0变量的真实字符串是整个脚本路径时,程序中就会使用整个路径,而不仅仅是程序名.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| vim tesh.sh name=`basename $0` echo the command entered is $name echo the command entered is $0 sh ./test.sh sh ~/test.sh if [ -n "$1" ] if [ $# -ne 2 ] for param in "$*" for param in "$@"
|
shell 命令
bash shell工具链中另一个工具是shift命令.bash shell提供了shell命令来帮助操作命令行参数.
默认情况下,shell命令会将参数变量减一,所以变量$2会移到$1,而变量$1的值会被删除并且无法恢复
变量$0是程序名不会改变.这是遍历命令行参数的另一个绝妙方法,尤其是你不知道有多少参数时.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| vim test1.sh count=1 while [ -n "$1" ] do echo "Parameter #$count = $1" count=$(( $count+1 )) shift done sh ./test1.sh femn leipengkai yuyu
|
命令行选项(是跟在单破折线后面的单个字母,可以修改命令行为的单字母值)
当脚本看到”–” ,脚本会安全地将剩下的命令行参数当做参数来处理而不是选项.
你可以像处理命令行参数一样处理命令行选项
你经常遇到想在脚本中同时使用选项和参数的情况.
Linux处理这个问题的标准方式是用特殊的字符将二者分开,该字符(–双破折线)会告诉脚本选项何时结束,以及普通参数何时开始.
getopt命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| getopt -q ab:cd -a -b test1 -cde test2 test3 vim test2.sh while [ -n "$1" ] do case "$1" in -a) echo "Found the -a options";; -b) param="$2" echo "Found the -b options,with parameter value $param" shift;; -c) echo "Found the -c options";; --) shift break;; *) echo "$1 is not an optionso";; esac shift done count=1 for param in "$@" do echo "Parameter #$count: $param" count=$(( $count +1 )) done sh ./test2.sh -b test2 -a -d sh ./test2.sh -b test2 -a -b
|
你可以在脚本中使用getopt命令来格式化输入脚本的任何命令行选项和参数,但用起来略微复杂.
1
| set -- `getopt -q ab:cd "$@" ` options parameters用"$@"
|
该方法将原本的脚本的命令行参数传给getopt命令,之后再将getopt的输出传给set命令,用getopt命令格式化后的命令行参数来替换原始命令行参数.
set 命令的选项之一是双破折线,它会将命令行参数替换成set命令的命令行的值.
1 2 3 4 5 6 7 8 9 10 11 12 13
| sh ./test2.sh -ac set -- `getopt -q ab:c "$@"` while [ -n "$1" ] sh ./test2.sh -a -b test1 -cd test2 test3
|
但getopt命令并不擅长处理带空格的参数值,它会将空格当做参数分隔符,而不是根据双引号将二者当作一个参数.
getopts命令扩展了getopt命令的功能:getopts optstring variable
与getopt命令将命令行上找到的选项和参数处理后只生成一个输出不同,getopts命令能够和已有的shell参数变量对应地顺序工作.
getopts命令将当前参数保存在命令行中定义的variable中.它会用到两个环境变量,如果选项需要参数值,OPTARG环境变量就会保存这个值.OPTIND环境变量保存了参数列表中getopts正在处理的参数位置.
获得用户输入
你想在运行脚本时,问一个问题,并等运行脚本的人来回答.bash shell为此提供了read命令.
read命令接受从标准输入(键盘)或另一个文件描述符的输入.
echo -n -n会移除字符串末尾的换行符,允许脚本用户紧跟其后输入数据,而不是下一行
read -n1 -t 5 -p 接受单个字符后退出而不必回车,超时,直接在read命令行提示符
read -s隐藏方式读取,实际上数据会被显示,只是read命令会将文本颜色设成跟背景色一样.
1 2 3 4 5 6
| vim test.sh #!/bin/bash read -p "ENter your name:" name echo "Hello $name, Welcome"
|
从文件中读取.每次调用read命令会从文件中读取一行文本,当文件没能内容时,read命令会退出并返回一个非零的状态码.
最常见的方法是将文件通过cat命令后的输出通过管道直接传给含有read命令的while命令.cat test |while read line