赞
踩
第11章构建基础脚本
第12章结构化命令
第13章更多的结构化命令
第14章处理用户输入
第15章呈现数据
第16章脚本控制
让两个命令一起运行,可以将其放在同一行中,彼此用分号隔开
$ date ; who
在创建shell 脚本文件时,必须在文件的第一行指定要使用的shell,格式如下:
#!/bin/bash
在普通的shell 脚本中,#用作注释行,#后面的惊叹号会告诉shell 用哪个shell 来运行脚本。
PATH 环境变量被设置为用于在其中查找命令的一系列目录。采用下列两种方法之一:
$ echo "This is a test to see if you're paying attention"
$ cat test1
#!/bin/bash
# This script displays the date and who's logged on
echo The time and date are:
date
echo "Let's see who's logged into the system: "
who
$
用set 命令显示一份完整的当前环境变量列表
在脚本中,可以在环境变量名之前加上$来引用这些环境变量
用户自定义变量可以通过$引用
$ cat test3
#!/bin/bash
# testing variables
days=10
guest="Katie"
echo "$guest checked in $days days ago"
$
有两种方法可以将命令输出赋给变量。
反引号(`)
$()格式
$ cat test5
#!/bin/bash
testing=$(date)
echo "The date and time are: " $testing
$
变量testing 保存着date 命令的输出,然后会使用echo 命令显示出该变量的值。
11.5.1 输出重定向
最基本的重定向会将命令的输出发送至文件。bash shell 使用大于号(>)来实现该操作。如果输出文件已存在,则重定向运算符会用新数据覆盖已有的文件。
$ date > test6
不想覆盖文件原有内容,而是想将命令输出追加到已有文件中,可以用双大于号(>>)来追加数据
$ date >> test6
11.5.2 输入重定向
输入重定向会将文件的内容重定向至命令,而不是将命令输出重定向至文件。
输入重定向运算符是小于号(<):command < inputfile
wc 命令可以统计数据中的文本。在默认情况下,它会输出3 个值
文本的行数
文本的单词数
文本的字节数
需要将一个命令的输出作为另一个命令的输入,无须将命令输出重定向至文件,可以将其直接传给另一个命令。这个过程称为管道连接(piping),单个竖线(|)来实现该操作,管道可以串联的命令数量没有限制。可以持续地将命令输出通过管道传给其他命令来细化
操作。
$ rpm -qa | sort | more
这行命令序列会执行rpm 命令,将其输出通过管道传给sort 命令,然后再将sort 的输出
通过管道传给more 命令来显示,在显示完一屏信息后暂停。
编程语言的另一项至关重要的特性是数学运算能力
门用于处理数学表达式的命令:expr,该命令可在命令行中执行数学运算,但是特别笨拙。
$ expr 1 + 5
许多expr 命令运算符在shell 中另有他意(比如*)此时需要使用方括号。在使用方括号执行数学运算时,无须担心shell 会误解乘号或其他符号。shell 清楚方括号内的星号不是通配符。
bash 计算器实际上是一种编程语言,允许在命令行中输入浮点数表达式,然后解释并计算该
表达式,最后返回结果。
在shell 提示符下通过bc 命令访问bash 计算器,要退出bash 计算器,必须输入quit。
- $ bc
- bc 1.06.95
- Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
- This is free software with ABSOLUTELY NO WARRANTY.
- For details type 'warranty'.
- 12 * 5.4
- 64.8
- 3.156 * (3 + 5)
- 25.248
- quit
- $
可以用命令替换来运行bc 命令,将输出赋给变量,基本格式如下
variable=$(echo "options; expression" | bc)
- $ cat test11
- #!/bin/bash
- var1=20
- var2=3.14159
- var3=$(echo "scale=4; $var1 * $var1" | bc)
- var4=$(echo "scale=4; $var3 * $var2" | bc)
- echo The final result is $var4
- $
涉及更多的数字。如果要进行大量运算,那么在一个命令行中列出多个表达式容易让人犯晕。最好的办法是使用内联输入重定向,它允许直接在命令行中重定向数据,字符串 EOF 标识了内联重定向数据的起止。基本格式如下:
variable=$(bc << EOF
options
statements
expressions
EOF
)
- $ cat test12
- #!/bin/bash
- var1=10.46
- var2=43.67
- var3=33.2
- var4=71
- var5=$(bc << EOF
- scale = 4
- a1 = ( $var1 * $var2)
- b1 = ($var3 * $var4)
- a1 + b1
- EOF
- )
- echo The final answer for this mess is $var5
- $
shell 中运行的每个命令都使用退出状态码来告诉shell 自己已经运行完毕。退出状态码是一
个0~255 的整数值,在命令结束运行时由其传给shell。你可以获取这个值并在脚本中使用。
Linux 提供了专门的变量$?来保存最后一个已执行命令的退出状态码。对于需要进行检查的
命令,必须在其运行完毕后立刻查看或使用$?变量。
- #执行date命令,对于成功结束的命令,其退出状态码是0。
-
- $ date
- Mon Jun 01 16:01:30 EDT 2020
- $ echo $?
- 0
- $
-
无效命令会返回退出状态码127。
在默认情况下,shell 脚本会以脚本中的最后一个命令的退出状态码退出
可以改变这种默认行为,返回自己的退出状态码。exit 命令允许在脚本结束时指定一个退出状态码:1. 自定义参数或者2. 变量的数值,但是使用这个功能时要小心,因为退出状态码最大只能是255。
- # exit 命令允许在脚本结束时指定自定义参数作为退出状态码
- $ cat test13
- #!/bin/bash
- # testing the exit status
- var1=10
- var2=30
- var3=$[ $var1 + $var2 ]
- echo The answer is $var3
- exit 5
- $
- 当你检查脚本的退出状态码时,就会看到传给exit 命令的参数值:
- $ chmod u+x test13
- $ ./test13
- The answer is 40
- $ echo $?
- 5
- $
-
-
-
- # exit 命令允许在脚本结束时指定变量作为退出状态码
- $ cat test14b
- #!/bin/bash
- # testing the exit status
- var1=10
- var2=30
- var3=$[ $var1 * $var2 ]
- echo The value is $var3
- exit $var3
- $
- 现在运行它会得到如下输出:
- $ ./test14b
- The value is 300
- $ echo $?
- 44
- $
bash shell 的if 语句会运行if 之后的命令。如果该命令的退出状态码为0(命令成功运行),那么位于then 部分的命令就会被执行。如果该命令的退出状态码是其他值,则then 部分的命令不会被执行,bash shell 会接着处理脚本中的下一条命令。fi 语句用来表示if-then 语句到此结束。if-then 语句的格式如下:
if command
then
commands
fi
当if 语句中的命令返回退出状态码0 时,then 部分中的命令会被执行,这跟普通的if-then
语句一样。当if 语句中的命令返回非0 退出状态码时,bash shell 会执行else 部分中的命令。if-then-else 语句在语句中提供了另外一组命令:
if command
then
commands
else
commands
fi
test 命令可以在if-then 语句中测试不同的条件。如果test 命令中列出的条件成立,那么
test 命令就会退出并返回退出状态码0。。如果条件不成立,那么test 命令就会退出并返回非0 的退出状态码,这使得if-then 语句不会再被执行。
- $ cat numeric_test.sh
- #!/bin/bash
- # Using numeric test evaluations
- #
- value1=10
- value2=11
- #
- if [ $value1 -gt 5 ]
- then
- echo "The test value $value1 is greater than 5."
- fi
- #
- if [ $value1 -eq $value2 ]
- then
- echo "The values are equal."
- else
- echo "The values are different."
- fi
- $
书写例子:
#
string1=soccer
string2=zorbfootball
#
if [ $string1 \> $string2 ]
if [ $string1 > $string2 ]
-d 测试会检查指定的目录是否存在于系统中
- #
- jump_directory=/home/Torfa
- #
- if [ -d $jump_directory ]
- ...
-e 测试允许在使用文件或目录前先检查其是否存在
- $ cat update_file.sh
- #!/bin/bash
- # Check if either a directory or file exists
- #
- location=$HOME
- file_name="sentinel"
- #
- if [ -d $location ]
- then
-e 测试可用于文件和目录。
- #!/bin/bash
- # Check if object exists and is a directory or a file
- #
- object_name=$HOME
- #
- if [ -e $object_name ]
- then
if-then 语句允许使用布尔逻辑将测试条件组合起来。可以使用以下两种布尔运算符。
[ condition1 ] && [ condition2 ]
[ condition1 ] || [ condition2 ]第一种布尔运算使用布尔运算符AND 来组合两个条件。要执行then 部分的命令,两个条件
都必须满足。第二种布尔运算使用OR 布尔运算符来组合两个条件。如果任意条件为真,那么then 部分
的命令就会执行。
单括号形式的test 命令格式如下:
(command)
在bash shell 执行command 之前,会先创建一个子shell,然后在其中执行命令。如果命令成
功结束,则退出状态码(参见第11 章)会被设为0,then 部分的命令就会被执行。如果命令的
退出状态码不为0,则不执行then 部分的命令。
警告:当你在if test 语句中使用进程列表(参见第5 章)时,可能会出现意料之外的结果。
哪怕进程列表中除最后一个命令之外的其他命令全都失败,子shell 仍会将退出状态码设
为0,then 部分的命令将得以执行。
双括号命令允许在比较过程中使用高级数学表达式
注意,双括号中表达式的大于号不用转义。这是双括号命令又一个优越性的体现。
例子:if (( $val1 ** 2 > 90 ))
双方括号命令提供了针对字符串比较的高级特性
在进行模式匹配时,可以定义通配符或正则表达式(参见第20 章)来匹配字符串:
例子: if [[ $BASH_VERSION == 5.* ]]
case 命令会将指定变量与不同模式进行比较。如果变量与模式匹配,那么shell 就会执行为
该模式指定的命令。你可以通过竖线运算符在一行中分隔出多个模式。星号会捕获所有与已知模
式不匹配的值。有了case 命令,就无须再写大量的elif 语句来检查同一个变量的值了。case 命令会采用列表格式来检查变量的多个值,case格式如下:
case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac
- $ cat ShortCase.sh
- #!/bin/bash
- # Using a short case statement
- #
- case $USER in
- rich | christine)
- echo "Welcome $USER"
- echo "Please enjoy your visit.";;
- barbara | tim)
- echo "Hi there, $USER"
- echo "We're glad you could join us.";;
- testing)
- echo "Please log out when done with test.";;
- *)
- echo "Sorry, you are not allowed here."
- esac
- $
提供了for 命令,以允许创建遍历一系列值的循环。每次迭代都使用其中一个值来执行已定义好的一组命令。for 命令的基本格式如下:
for var in list
do
commands
done
- $ cat test1b
- #!/bin/bash
- # testing the for variable after the looping
- for test in Alabama Alaska Arizona Arkansas California Colorado
- do
- echo "The next state is $test"
- done
- echo "The last state we visited was $test"
- test=Connecticut
- echo "Wait, now we're visiting $test"
- $ ./test1b
- The next state is Alabama
- The next state is Alaska
- The next state is Arizona
- The next state is Arkansas
- The next state is California
- The next state is Colorado
- The last state we visited was Colorado
- Wa
最后,还可以用for 命令来自动遍历目录中的文件。为此,必须在文件名或路径名中使用
通配符,这会强制shell 使用文件名通配符匹配(file globbing)。文件名通配符匹配是生成与指定
通配符匹配的文件名或路径名的过程。
- $ cat test6
- #!/bin/bash
- # iterate through all the files in a directory
- for file in /home/rich/test/*
- do
- if [ -d "$file" ]
- then
- echo "$file is a directory"
- elif [ -f "$file" ]
- then
- echo "$file is a file"
- fi
- done
- $ ./test6
- /home/rich/test/dir1 is a directory
- /home/rich/test/myprog.c is a file
- /home/rich/test/myprog is a file
- /home/rich/test/myscript is a file
- /home/rich/test/newdir is a directory
- /home/rich/test/newfile is a file
- /home/rich/test/newfile2 is a file
- /home/rich/test/testdir is a directory
- /home/rich/test/testing is a file
- /home/rich/test/testprog is a file
- /home/rich/test/testprog.c is a file
- $
迭代条件使用标准的数学符号定义。例如,考虑下面的
C 语言代码:
for (i = 0; i < 10; i++)
{
printf("The next number is %d\n", i);
}
- $ cat test8
- #!/bin/bash
- # testing the C-style for loop
- for (( i=1; i <= 10; i++ ))
- do
- echo "The next number is $i"
- done
- $ ./test8
- The next number is 1
- The next number is 2
- The next number is 3
- The next number is 4
- The next number is 5
- The next number is 6
- The next number is 7
- The next number is 8
- The next number is 9
- The next number is 10
- $
仿C 语言的for 命令也允许为迭代使用多个变量。循环会单独处理每个变量,你可以为每
个变量定义不同的迭代过程
- $ cat test9
- #!/bin/bash
- # multiple variables
- for (( a=1, b=10; a <= 10; a++, b-- ))
- do
- echo "$a - $b"
- done
- $ ./test9
- 1 - 10
- 2 - 9
- 3 - 8
- 4 - 7
- 5 - 6
- 6 - 5
- 7 - 4
- 8 - 3
- 9 - 2
- 10 - 1
- $
while 命令在某种程度上糅合了if-then 语句和for 循环。while 命令允许定义一个要
测试的命令,只要该命令返回的退出状态码为0,就循环执行一组命令。
只要测试条件成立,while 命令就会不停地循环执行定义好的命令。必须在这些命令中修
改测试条件中用到的变量,否则就会陷入死循环。
while 命令的格式如下:
while test command
do
other commands
done
- $ cat test10
- #!/bin/bash
- # while command test
- var1=10
- while [ $var1 -gt 0 ]
- do
- echo $var1
- var1=$[ $var1 - 1 ]
- done
- $ ./test10
- 10
- 9
- 8
- 7
- 6
- 5
- 4
- 3
- 2
- 1
- $
在含有多个命令的while 语句中,在每次迭代时所有的测试命令都会被执行,包括最后一个测试命令失败的末次迭代。要小心这一点。另一处要留意的是该如何指定多个测试命令。
注意要把每个测试命令都单独放在一行中。
与while 命令工作的方式完全相反,until 命令要求指定一个返回非0 退出状态码的测试
命令。只要测试命令的退出状态码不为0,bash shell 就会执行循环中列出的命令。
until 命令的格式如下:
until test commands
do
other commands
done
与while 命令类似,你可以在until 命令语句中放入多个test command。最后一个命令
的退出状态码决定了bash shell 是否执行已定义的other commands。
- $ cat test12
- #!/bin/bash
- # using the until command
- var1=100
- until [ $var1 -eq 0 ]
- do
- echo $var1
- var1=$[ $var1 - 25 ]
- done
- $ ./test12
- 100
- 75
- 50
- 25
- $
循环语句可以在循环内使用任意类型的命令,包括其他循环命令,这称为嵌套循环。注意,在使用嵌套循环时是在迭代中再进行迭代,命令运行的次数是乘积关系。不注意这点有可能会在
格式
then
break
在处理多个循环时,break 命令会自动结束你所在的最内层循环
有时你位于内层循环,但需要结束外层循环。break 命令接受单个命令行参数:
break n
其中n 指定了要跳出的循环层级。在默认情况下,n 为1,表明跳出的是当前循环。如果将n 设
为2,那么break 命令就会停止下一级的外层循环。
continue 命令可以提前中止某次循环,但不会结束整个循环,格式:
then
continue
then
continue 2
在shell 脚本中,可以对循环的输出使用管道或进行重定向。这可以通过在done 命令之后
添加一个处理命令: > output.txt
- $ cat test23
- #!/bin/bash
- # redirecting the for output to a file
- for (( a = 1; a < 10; a++ ))
- do
- echo "The number is $a"
- done > test23.txt
- echo "The command is finished."
向shell 脚本传递数据的最基本方法是使用命令行参数
$ ./addem 10 30
本例向脚本addem 传递了两个命令行参数(10 和30)
bash shell 会将所有的命令行参数都指派给称作位置参数(positional parameter)的特殊变量
在shell 脚本中,可以像使用其他变量一样使用$1 变量。shell 脚本会自动将命令行参数的值
分配给位置变量,无须做任何特殊处理。
如果需要输入更多的命令行参数,则参数之间必须用空格分开。shell 会将其分配给对应的位
置变量
- $ cat positional2.sh
- #!/bin/bash
- # Using two command-line parameters
- #
- product=$[ $1 * $2 ]
- echo The first parameter is $1.
- echo The second parameter is $2.
- echo The product value is $product.
- exit
- $
- $ ./positional2.sh 2 5
- The first parameter is 2.
- The second parameter is 5.
- The product value is 10.
- $
使用位置变量$0 获取在命令行中运行的shell 脚本名。这在编写包含多种功能或生成日
志消息的工具时非常方便
- $ cat countparameters.sh
- #!/bin/bash
- # Counting command-line parameters
- #
- if [ $# -eq 1 ]
- then
- fragment="parameter was"
- else
- fragment="parameters were"
- fi
- echo $# $fragment supplied.
- exit
- $
- $ ./countparameters.sh
- 0 parameters were supplied.
- $
- $ ./countparameters.sh Hello
- 1 parameter was supplied.
- $
特殊变量$#含有脚本运行时携带的命令行参数的个数。你可以在脚本中的任何地方使用这
个特殊变量
$*变量和$@变量可以轻松访问所有参数,它们各自包含了所有的命令行参数。$*变量会将所有参数视为单个参数,而$@变量会单独处理每个参数
- $ cat grabdisplayallparams.sh
- #!/bin/bash
- # Exploring different methods for grabbing all the parameters
- #
- echo
- echo "Using the \$* method: $*"
- count=1
- for param in "$*"
- do
- echo "\$* Parameter #$count = $param"
- count=$[ $count + 1 ]
- done
- #
- echo
- echo "Using the \$@ method: $@"
- count=1
- for param in "$@"
- do
- echo "\$@ Parameter #$count = $param"
- count=$[ $count + 1 ]
- done
- echo
- exit
- $
14.4.1 查找选项
14.4.2 使用getopt命令
14.4.3 使用getopts命令
read
- $ cat askname.sh
- #!/bin/bash
- # Using the read command
- #
- echo -n "Enter your name: "
- read name
- echo "Hello $name, welcome to my script."
- exit
- $
- $ ./askname.sh
- Enter your name: Richard Blum
- Hello Richard Blum, welcome to my script.
- $
14.6.2 超时 #
14.6.3 无显示读取 #
14.6.4 从文件中读取 #
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。