用什么程序做网站好,什么叫网站根目录,直播网站怎么做啊,静态网站被挂马环境变量
全局、局部环境变量
用户变量#xff08;局部变量#xff09;#xff1a;修改的设置只对某个用户的路径或执行起作用#xff1b; 系统变量#xff08;全局变量#xff09;#xff1a;影响范围是整个系统 #xff1b; 系统环境变量基本上都是使用全大写…环境变量
全局、局部环境变量
用户变量局部变量修改的设置只对某个用户的路径或执行起作用 系统变量全局变量影响范围是整个系统 系统环境变量基本上都是使用全大写字母以区别于普通用户的环境变量。 查看
# 查看全局变量
$ env
$ printenv
$ printenv HOME # 要显示个别环境变量的值可以使用printenv命令但是不能用env命令# 查看当前环境变量set命令会显示某个特定进程设置的所有环境变量包括局部变量、全局变量以及用户定义变量
$ setenv、printenv和set之间的差异 1、set命令会显示出全局变量、局部变量以及用户定义变量。它还会按照字母顺序对结果进行排序。 2、env和printenv命令不会对变量排序也不会输出局部变量和用户定义变量。在这种情况下env和printenv的输出是重复的。不过env命令有一个printenv没有的功能这使得它要更有用一些。 设置变量
# 设置局部变量
$ my_variable Hello World# 设置全局变量可以通过export命令使局部变量变成全局变量
$ export my_variable# 删除环境变量
$ unset my_variable注意如果在子进程中删除了一个全局环境变量 这只对子进程有效。该全局环境变量在父进程中依然可用。 设置 PATH
$ PATH$PATH:/home/xx/xx 对PATH变量的修改只能持续到退出或重启系统。这种效果并不能一直持续。
环境变量持久化
当登录Linux系统时bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令
/etc/profile$HOME/.bash_profile$HOME/.bashrc$HOME/.bash_login$HOME/.profile /etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行这个启动文件。另外4个启动文件是针对用户的可根据个人需求定制。 1、对全局环境变量来说可能更倾向于将新的或修改过的变量设置放在**/etc/profile**文件中但这可不是什么好主意。如果你升级了所用的发行版这个文件也会跟着更新那你所有定制过的变量设置可就都没有了。
2、最好是在**/etc/profile.d目录中创建一个以.sh结尾的文件**。把所有新的或修改过的全局环境变量设置放在这个文件中。
3、在大多数发行版中存储个人用户永久性bash shell变量的地方是** H O M E / . b a s h r c ∗ ∗ 文件。这一点适用于所有类型的 s h e l l 进程。但如果设置了 ∗ ∗ B A S H E N V ∗ ∗ 变量那么除非它指向的是 ∗ ∗ HOME/.bashrc**文件。这一点适用于所有类型的shell进程。但如果设置了**BASH_ENV**变量那么除非它指向的是** HOME/.bashrc∗∗文件。这一点适用于所有类型的shell进程。但如果设置了∗∗BASHENV∗∗变量那么除非它指向的是∗∗HOME/.bashrc**否则你应该将非交互式shell的用户变量放在别的地方。 alias命令设置就是不能持久的可以把alias设置放在 $HOME/.bashrc 启动文件中使其效果永久化。 数组
要给某个环境变量设置多个值可以把值放在括号里值与值之间用空格分隔。
$ mytest(one two three four five)
$ echo ${mytest[0]}
one
$ echo ${mytest[1]}
two
$ echo ${mytest[*]}
one two three four five
#改变某个索引值位置的值
$ mytest[2]seven
$ echo ${mytest[*]}
one two seven four five
#删除
$ unset mytest[2] //这里只是删除了值[2]变成空了而已
$ unset mytest数学运算
expr 命令
expr命令允许在命令行 上处理数学表达式但需要转义特殊字符。
#!/bin/bash
var110
var220
var3$(expr $var2 / $var1)
echo The result is $var3使用方括号 [ ]
不需要转义特殊字符但只支持整数运算。
#!/bin/bash
var1100
var250
var345
var4$[$var1 * ($var2 - $var3)]
echo The final result is $var4内建计算器bc
bash计算器能够识别整数和浮点数、变量、注释、表达式、编程语句、函数
#!/bin/bash
var1100
var245
var3$(echo scale4; $var1 / $var2 | bc) # scale小数位数默认0
echo The answer for this is $var3如果需要进行大量运算EOF字符串标识了重定向给bc命令的数据的起止#!/bin/bash
var110.46
var243.67
var333.2
var471 var5$(bc EOF
scale 4
a1 ( $var1 * $var2)
b1 ($var3 * $var4)
a1 b1
EOF
)
echo $var5退出状态码
查看退出状态
$?Linux退出状态码
状态码描述0执行成功1一般性未知错误参数有误2不适合的shell命令126命令不可执行无权限127没有找到命令128无效的退出参数128x与Linux信号x相关的严重错误130通过Ctrlc终止的命令255正常范围之外的退出状态码
自定义退出状态码
默认情况下shell脚本会以脚本中的最后一个命令的退出状态码退出。 exit 命令允许在脚本结束时指定一个退出状态码。例如
$ cat test13
#!/bin/bash
# testing the exit status
var110
var230
var3$[$var1 $var2]
echo The answer is $var3
exit 5 # 指定退出状态码
# exit var2 # 可以指定变量$ chmod ux test13
$ ./test13
The answer is 40
$ echo $?
5 注意退出状态码最大只能是255区间为0255 字段分隔符 IFS
IFS环境变量定义了bash shell用作字段分隔符的一系列字符。 默认情况下bash shell会将下列字符当作字段分隔符 空格、制表符、换行符如果bash shell在数据中看到了这些字符中的任意一个它就会假定这表明了列表中一个新数据字段的开始。 可以将IFS的值也可以设为其他也可以设置多个
# 将IFS的值设为冒号(:)
IFS:
# 设置多个将换行符($\n)、冒号(:)、分号(;)和双引号()作为字段分隔符
IFS$\n:;#!/bin/bash
# reading values from a file
filestates
# 设置字段分割为换行符
IFS$\n
for f in $(cat $file)
do echo Visit beautiful $f
done 在处理代码量较大的脚本时可能在一个地方需要修改IFS的值然后忽略这次修改在脚本的其他地方继续沿用IFS的默认值。一个可参考的安全实践是在改变IFS之前保存原 来的IFS值之后再恢复它。 这种技术可以这样实现
IFS.OLD$IFS
IFS$\n
[代码...]
IFS$IFS.OLD 这就保证了在脚本的后续操作中使用的是IFS的默认值。
判断条件
test 命令
如果test命令中列出的条件成立test命令就会退出并返回退出状态码0 test命令可以判断三类条件数值比较、字符串比较、文件比较
if test condition
then commands
fi[ ] 替代test命令
[ ] 定义测试条件等同 test 命令。
if [ condition ] # condition 前后需要加一个空格
then commands
fi数值比较
-eq 等于-ne 不等-gt 大于-ge 大于等于-lt 小于-le 小于等于 bash shell只能处理整数不支持浮点值比较。 字符串比较
str1 str2 检查str1是否和str2相同str1 ! str 检查str1是否和str2不同str1 str2 检查str1是否比str2小str1 str2 检查str1是否比str2大-n str1 检查str1的长度是否不为0-z str1 检查str1的长度是否为0 字符串比较大小时有两个问题 1、大于号和小于号必须转义否则shell会把它们当作重定向符号把字符串值当作文件名 2、大于和小于顺序和sort命令所采用的不同顺序相反 空的和未初始化的变量也可以用-n、-z判断。 文件比较
-d file 检查file是否存在且是否为目录-f file 检查file是否存在且是否为文件-e file 检查file目录或文件是否存在-r file 检查file目录或文件是否存在并可读-s file 检查file目录或文件是否存在并非空-w file 检查file目录或文件是否存在并可写-x file 检查file目录或文件是否存在并可执行-O file 检查file目录或文件是否存在并属当前用户所有-G file 检查file目录或文件是否存在并且默认组与当前用户相同file1 -nt file2 检查file1是否比file2新file1 -ot file2 检查file1是否比file2旧
布尔运算( 、|| )
A B 当A命令执行成功才执行BA || B 仅当A执行失败才执行B 布尔逻辑是一种能够将可能的返回值简化为TRUE或FALSE的方法。 [ A B ] 使用AND布尔运算符来组合两个条件。两个条件都必须满足then部分的命令才会执行。 [ A || B ] 使用OR布尔运算符来组合两个条件。任意条件为TRUEthen部分的命令就会执行。
双括号 (( ))
双括号命令允许你在比较过程中使用高级数学表达式。 只能进行整数运算不能对小数浮点数或者字符串进行运算。
(( expression )) # expression 可以是任意的数学赋值或比较表达式表达式可以有多个多个表达式之间以逗号,分隔。对于多个表达式的情况以最后一个表达式的值作为整个 (( )) 命令的执行结果 echo $((a35, ba10)) 输出15 可以使用 $ 获取 (( )) 命令的结果这和使用 获得变量值类似 a 获得变量值类似a 获得变量值类似a((1066)在 (( )) 内使用变量无需加上 前缀会自动解析变量名 ( ( a b ) ) 但要获取结果时需要加上 c ∗ ∗ 前缀会自动解析变量名((ab))但要获取结果时需要加上c** 前缀会自动解析变量名((ab))但要获取结果时需要加上c∗∗**((ab))
除了支持简单的加减乘除外还支持其他运算符
符合描述val后增val–后减val先增–val先减!逻辑求反~位求反**幂运算左位移右位移位布尔和|位布尔或逻辑和||逻辑或
可以在 if 语句中用双括号命令也可以在脚本中的普通命令里使用来赋值
$ cat test.sh
#!/bin/bash val110
if (( $val1 ** 2 90 )) ; then(( val2 $val1 ** 2 )) echo The square of $val1 is $val2
fi $ ./test.sh
The square of 10 is 100注意不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性。 双方括号 [[ ]]
双方括号命令提供了针对字符串比较的高级特性。
[[ expression ]] 双方括号里的expression使用了test命令中采用的标准字符串比较。但它提供了test命 令未提供的另一个特性模式匹配。在模式匹配中双方括号中可以定义一些正则表达式来匹配字符串
$ cat test.sh
#!/bin/bash if [[ $USER r* ]]
then echo Hello $USER
else echo Sorry, I do not know you
fi $ ./test.sh
Hello rich 注意不是所有的shell都支持双方括号 小数比较
$ echo 1.8 1.6 |bc # 不成立输出 0
0
$ echo 1.8 1.6 |bc # 成立输出 1
1#!/bin/basha0.23
b0.9if [ $(echo $a $b | bc) 1 ]; thenecho ab
elseecho ab
fi结构化命令
if-then
与其他编程语言不同shell 的 if 语句会运行 if 后面的命令如果该命令的退出状态码是0 该命令成功运行位于then部分的命令就会被执行。如果该命令的退出状态码是其他值就不会被执行会继续执行脚本中的下一个命令。
if command ; thencommands
elif command ; thencommands
elsecommands
fi例
$ cat test1.sh
#!/bin/bash
# testing the if statement
if pwd
then echo It worked
fi# 这个脚本在if行采用了pwd命令。如果命令成功结束再执行echo语句
$ ./test1.sh
/home/Christine
It worked case 命令
case命令为变量每个可能的值指定不同的选项。
case variable in pattern1 | pattern2) commands1;; pattern3) commands2;; *) default commands;;
esac case 匹配不同的某值如果匹配成功则执行它下面的命令直到 ;; 为止
#!/bin/bash
echo 输入 1 到 3 之间的数字:
echo 你输入的数字为:
read Num
case $Num in1) echo 你选择了 1 ;; # 23) 值内容可以加上可以有多个值2) echo 你选择了 2 ;;3) echo 你选择了 3 ;;*) echo 你没有输入 1 到 3 之间的数字 ;; # 如以上都不匹配则执行这条
esac #结束echo test case end #输出内容for 循环
#!/bin/bash
listA B C D
list$list E # 这是向变量中存储的已有文本字符串尾部添加文本的一个常用方法
for test in $list
do echo The next number is $test
done
echo The last number is $test # for结束后test变量会一直保持最后一次迭代的值# 从命令读取值遍历
#!/bin/bash
filetest.txt
for f in $(cat $file)
do echo Visit beautiful $f
done for命令默认用空格、制表符、换行符来划分列表中的每个值。如果在单独的数据值中有空格就必须用双引号将这些值圈起来。 也可以将do语句和for语句放在同一行但必须用分号将其同列表中的值分开for var in list; do 用通配符遍历目录
# 遍历目录文件支持多个目录
for file in /home/rich/.b* /home/rich/badtest/*
do if [ -d $file ] # 将$file变量用双引号圈起来预防有带空格的目录then echo $file is a directory elif [ -f $file ] then echo $file is a file else echo $file doesnt exist fi
done 在Linux中目录名和文件名中包含空格当然是合法的。要适应这种情况应该将$file变量用双引号圈起来。如果不这么做遇到含有空格的目录名或文件名时就会有错误产生 ./test6: line 6: [: too many arguments for迭代 i
#!/bin/bash
for (( i1; i 10; i ))
do echo The next number is $i
done$ ./test8
The next number is 1
The next number is 2
...# 可以使用多个变量
#!/bin/bash
# multiple variables
for (( a1, b10; a 10; a, b-- ))
do echo $a - $b
done while 循环
while命令允许定义一个要测试的命令条件只要定义的测试命令返回非0退出状态码时while命令会停止循环。
while命令允许你在while语句行定义多个测试命令。只有最后一个测试命令不成立时停止循环。
#!/bin/bash
var110
while echo $var1 [ $var1 -ge 0 ] # 注意每个测试命令都在单独的一行上
do echo This is inside the loop var1$[ $var1 - 1 ]
done until 循环
until命令和while命令工作的方式完全相反。until命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为0bash shell才会执行循环中列出的命令。 一旦测试命令返回了退出状态码0循环就结束了。
和while命令类似可以指定的多个测试命令只有在最后一个测试命令成立时停止循环。
#!/bin/bash
var1100
until echo $var1 [ $var1 -eq 0 ]
do echo Inside the loop: $var1 var1$[ $var1 - 25 ]
done 循环处理文件数据
通过修改IFS环境变量就能强制for命令将文件中的每行都当成单独的一个条目来处理即便数据中有空格也是如此。一旦从文件中提取出了单独的行可能需要再次利用循环来提取行中的数据。
#!/bin/bash
# 遍历/etc/passwd文件中的数据
IFS$\n
for entry in $(cat /etc/passwd)
do echo Values in $entry – # 先换行符间隔遍历IFS:for value in $entry # 然后冒号间隔遍历do echo $value done
done 这个脚本使用了两个不同的IFS值来解析数据。第一个IFS值解析出/etc/passwd文件中的单独的行。内部for循环接着将IFS的值修改为冒号允许你从/etc/passwd的行中解析出单独的值。
控制循环
break
跳出内部循环在处理多个循环时break命令会终止所在的最内层的循环。 即使内部循环通过break命令终止了外部循环依然继续执行。
#!/bin/bash
# breaking out of an inner loop
for (( a 1; a 4; a )) ; do echo Outer loop: $a for (( b 1; b 100; b )) ; do if [ $b -eq 4 ] then break fi echo Inner loop: $b done
donebreak n
跳出外部循环有时你在内部循环但需要停止外部循环 break n 。 其中n指定了要跳出的循环层级。默认n为1表明跳出的是当前的循环。如果你将 n设为2break命令就会停止下一级的外部循环。
#!/bin/bash
# breaking out of an outer loop
for (( a 1; a 4; a )) ; do echo Outer loop: $a for (( b 1; b 100; b )) ; do if [ $b -gt 4 ] then break 2 fi echo Inner loop: $b done
donecontinue
跳过某次循环可以提前中止某次循环中的命令但并不会完全终止整个循环。 和break命令一样continue命令也可以通过参数 n 指定哪一级循环continue n
#!/bin/bash
for (( var1 1; var1 15; var1 )) ; do if [ $var1 -gt 5 ] [ $var1 -lt 10 ] then continue fi echo Iteration number: $var1
done 处理循环的输出
可以对循环的输出使用管道或进行重定向可以在done命令之后添加一个处理命令。
for file in /home/rich/* ; do if [ -d $file ] then echo $file is a directory elif echo $file is a file fi
done output.txt # 输出重定向到某个文件中for state in A B C E D 2 1 ; do echo $state
done | sort # 对输出进行排序脚本参数及选项
参数$
参数说明$0当前脚本文件名$n传递给脚本或函数的参数n表示第几个参数。$#传递到脚本或函数的参数个数$*传递给脚本或函数的所有参数。如 “$*” 被双引号包含时会将所有的参数看做一份数据如 “$1 $2 … $n”$传递给脚本或函数的所有参数。如 “$” 被双引号包含时每个参数都看作一份数据如 “$1” “ 2 … 2 … 2…n”$$当前 Shell 进程 ID。对于 Shell 脚本就是这些脚本所在的进程 ID$!后台运行的最后一个进程的ID号$-显示Shell使用的当前选项与set命令功能相同。$?上个命令的退出状态或函数的返回值。0表示没有错误其他任何值表明有错误。 最后一个参数KaTeX parse error: Expected }, got # at position 3: {!#̲} 注意当命令行上没有任何参…#的值为0但${!#}变量会返回命令行用到的脚本名。 $* 和 $ 的区别
当 $* 和 不被双引号 ‘ ‘ 包围时它们之间没有任何区别都是将接收到的每个参数看做一份数据彼此之间以空格来分隔。但是当它们被双引号 ‘ ‘ 包含时就会有区别了 1 、 ‘ 不被双引号 包围时它们之间没有任何区别都是将接收到的每个参数看做一份数据彼此之间以空格来分隔。 但是当它们被双引号 包含时就会有区别了 1、 不被双引号‘‘包围时它们之间没有任何区别都是将接收到的每个参数看做一份数据彼此之间以空格来分隔。但是当它们被双引号‘‘包含时就会有区别了1、‘*“会将所有的参数从整体上看做一份数据而不是把每个参数都看做一份数据。 2、”$仍然将每个参数都看作一份数据彼此之间是独立的。
特殊变量$?
$? 是一个特殊变量用来获取上一个命令的退出状态或者上一个函数的返回值。
# 获取上一个命令的退出状态
if [ $1 100 ]
thenexit 0 #参数正确退出状态为0
elseexit 1 #参数错误退出状态1
fi# 获取函数的返回值
function add(){return expr $1 $2
}
add 23 50 #调用函数
echo $? #获取函数返回值:73命令状态码详情参见《自定义退出状态码》 函数返回值详情参见《返回值》 移动变量shift
shift命令会根据它们的相对位置来移动命令行参数。在使用shift命令时默认情况下它会将每个参数变量向左移动一个位置。所以变量$3 的值会移到$2中变量$2的值会移到$1中而变量$1的值则会被删除注意变量$0的值也 就是程序名不会改变。
这可以很好的遍历命令行参数尤其是在不知道到底有多少参数时。可以只操作第一个参数移动参数然后继续操作第一个参数。
# 遍历所有命令行参数
while [ -n $1 ] ; do # 当第一个参数的长度为零时循环结束echo Parameter $1shift # 也可以一次性移动多个位置shift nn指明要移动的位置数
doneshift n
也可以一次性移动多个位置只需要给shift命令提供一个参数指明要移动的位置数就行了。
$ cat test14.sh
#!/bin/bash
echo
echo Before moving parameters: $*
shift 2 # 向左移动两位
echo After moving first parameter: $1 $ ./test14.sh 1 2 3 4 5
Before moving parameters: 1 2 3 4 5
After moving first parameter: 3 通过使用shift命令的参数就可以轻松地跳过不需要的参数。 处理选项
处理简单选项
遍历所有参数内嵌case命令达到不同命令执行不同的处理。
# 在提取每个单独参数时用case语句来判断某个参数是否为选项
while [ -n $1 ] ; do case $1 in -a) echo Found the -a option ;; -b) echo Found the -b option ;; -c) echo Found the -c option ;; *) echo $1 is not an option ;; esac shift
done $ ./xx.sh -a -b -c -d
Found the -a option
Found the -b option
Found the -c option
-d is not an option
# 不管选项按什么顺序出现在命令行上这种方法都适用
$ ./xx.sh -d -c -a分离参数和选项
shell会用双破折线来表明选项列表结束。在双破折线–之后脚本就可以放心地将剩下的命令行参数当作参数
# 提取选项
while [ -n $1 ]
do case $1 in -a) echo Found the -a option ;; -b) echo Found the -b option;; -c) echo Found the -c option ;; --) shift break ;; *) echo $1 is not an option;; esac shift
done
# 提取参数
count1
for param in $
do echo Parameter #$count: $param count$[ $count 1 ]
done 遇到双破折线时脚本用break命令来跳出while循环。由于过早地跳出了循环需要再加一条shift命令来将双破折线移出参数变量。
# 不带“--”时所有参数都会当作命令行参数
$./xx.sh -c -a -b test1 test2 test3
Found the -c option
Found the -a option
Found the -b option
test1 is not an option
test2 is not an option
test3 is not an option# 当脚本遇到“--”时它会停止处理选项并将剩下的参数都当作命令行参数
$./xx.sh -c -a -b -- test1 test2 test3
Found the -c option
Found the -a option
Found the -b option
Parameter #1: test1
Parameter #2: test2
Parameter #3: test3处理带值的选项
# -b选项还需要一个额外的参数值
while [ -n $1 ] ; docase $1 in-a) echo Found the -a option;;-b) param$2echo Found the -b option, with parameter value $paramshift ;;-c) echo Found the -c option;;--) shiftbreak ;;*) echo $1 is not an option;;esacshift
donecount1
for param in $
doecho Parameter #$count: $paramcount$[ $count 1 ]
done# 执行
$./xx.sh -a -b test1 -d
Found the -a option
Found the -b option, with parameter value test1
-d is not an option
# 更换带值参数的位置也可以
$ ./xx.sh -b test1 -a -d
Found the -b option, with parameter value test1
Found the -a option
-d is not an option 在这个例子中case语句定义了三个它要处理的选项。-b选项还需要一个额外的参数值。 由于要处理的参数是$1额外的参数值就应该位于$2因为所有的参数在处理完之后都会被移出。只要将参数值从$2变量中提取出来就可以了。当然因为这个选项占用了两个参数位所以你还需要使用shift命令多移动一个位置。
但还有一些限制比如想将多个选项放进一个参数中不过 getopt 可以解决
$ ./xx.sh -ac
-ac is not an optiongetopt
getopt命令是一个在处理命令行选项和参数时非常方便的工具。它能够识别命令行参数 从而在脚本中解析它们时更方便。getopt命令可以接受一系列任意形式的命令行选项和参数并自动将它们转换成适当的格式。
命令格式 getopt optstring parameters
在每个需要参数值的选项字母后加一个冒号。getopt命令会基于你定义的optstring解析提供的参数
$ getopt ab:cd -a -b test1 -cd test2 test3
-a -b test1 -c -d -- test2 test3 optstring定义了四个有效选项字母a、b、c和d。冒号:被放在了字母b后面代表b选项需要一个参数值。当getopt命令运行时它会检查提供的参数列表-a -b test1 -cd test2 test3并基于提供的optstring进行解析。注意它会自动将-cd选项分成两个单独的选项并插入双破折线来分隔行中的额外参数。
如果指定了一个不在optstring中的选项默认情况下getopt命令会产生一条错误消息。
$ getopt ab:cd -a -b test1 -cde test2 test3
getopt: invalid option -- e -a -b test1 -c -d -- test2 test3 # 在命令后加-q选项可以忽略这条错误消息
$ getopt -q ab:cd -a -b test1 -cde test2 test3-a -b test1 -c -d -- test2 test3 注意getopt命令选项必须出现在optstring之前。 在脚本中使用getopt
可以在脚本中使用getopt来格式化脚本所携带的任何命令行选项或参数set命令实现用getopt命令生成的格式化后的版本来替换已有的命令行选项和参数。
set命令的选项之一是双破折线–它会将命令行参数替换成set命令的命令行值。然后该方法会将原始脚本的命令行参数传给getopt命令之后再将getopt命令的输出传给set命令用getopt格式化后的命令行参数来替换原始的命令行参数如下所示
set -- $(getopt -q ab:cd $) 现在原始的命令行参数变量的值会被getopt命令的输出替换而getopt已经为我们格式化好了命令行参数。利用该方法就可以写出能处理命令行参数的脚本
#!/bin/bash
set -- $(getopt -q ab:cd $) while [ -n $1 ] ; do case $1 in -a) echo Found the -a option ;; -b) param$2 echo Found the -b option, with parameter value $param shift ;; -c) echo Found the -c option ;; --) shift break ;; *) echo $1 is not an option;; esacshift
done count1
for param in $
do echo Parameter #$count: $param count$[ $count 1 ]
done# 可以处理合并选项了
$./xx.sh -ac
Found the -a option
Found the -c option
#
$ ./test18.sh -a -b test1 -cd test2 test3
Found the -a option
Found the -b option, with parameter value test1
Found the -c option
Parameter #1: test2
Parameter #2: test3 # 存在问题getopt命令并不擅长处理带空格和引号的参数值。它会将空格当作参数分隔符而不是根据双引号将二者当作一个参数。
$ ./xx.sh -a -b test1 -cd test2 test3 test4
Found the -a option
Found the -b option, with parameter value test1
Found the -c option
Parameter #1: test2
Parameter #2: test3
Parameter #3: test4 但getopt命令并不擅长处理带空格和引号的参数值。它会将空格当作参数分隔符而不是根据双引号将二者当作一个参数。不过还有另外一个办法能解决这个问题—— [getopts](#更高级的 getopts)。
更高级的 getopts
getopts 内建于bash shell。它比 getopt 多了一些扩展功能。 与getopt不同前者将命令行上选项和参数处理后只生成一个输出而 getopts 命令能够和已有的shell参数变量配合默契。 每次调用它时它一次只处理命令行上检测到的一个参数。处理完所有的参数后它会退出并返回一个大于0的退出状态码。这让它非常适合用解析命令行所有参数的循环中。
getopts optstring variable有效的选项字母都会列在optstring中如果选项要求有个参数值就在其后加一个冒号。要去掉错误消息的话可以在optstring之前加一个冒号。getopts命令将当前参数保存在命令行中定义的 variable 中。
getopts 命令会用到两个环境变量
OPTARG保存带值参数的值OPTIND保存下一个要处理的参数索引
当调用getopts时如果发现OPTIND索引处的选项有输入值则设置OPTARG并返回true即便没有输入值若索引处存在选项也会返回true同时为OPTIND递增1
while getopts :ab:c opt ; do case $opt ina) echo Found the -a option ;; b) echo Found the -b option, value: $OPTARG;; c) echo Found the -c option ;; *) echo Unknown option: $opt;; esac
done# ./xx.sh -ab test1 -c
Found the -a option
Found the -b option, with value test1
Found the -c option while语句定义了getopts命令指明了要查找哪些命令行选项以及每次迭代中存储它们的变量名opt。 getopts命令解析命令行选项时会移除开头的单破折线-所以在case定义中不用单破折线。
# 可以在参数值中包含空格
$./xx.sh -b test1 test2 -a
Found the -b option, with value test1 test2
Found the -a option # 可以将选项字母和参数值放在一起使用而不用加空格
$ ./xx -abtest1
Found the -a option
Found the -b option, with value test1# 能够将命令行上找到的所有未定义的选项统一输出成问号
$ ./xx.sh -acde
Found the -a option
Found the -c option
Unknown option: ?
Unknown option: ? 在getopts处理每个选项时会将OPTIND环境变量值增一。在getopts完成处理时可以使用shift命令和OPTIND值来移动参数。OPTIND为下一个要处理的参数索引
#!/bin/bash
while getopts :ab:cd opt ; do case $opt in a) echo Found the -a option ;; b) echo Found the -b option, with value $OPTARG ;; c) echo Found the -c option ;; d) echo Found the -d option ;; *) echo Unknown option: $opt ;; esac
done
#
shift $[ $OPTIND - 1 ]
#
echo
count1
for param in $
do echo Parameter $count: $param count$[ $count 1 ]
done $ ./xx.sh -a -b test1 -d test2 test3
Found the -a option
Found the -b option, with value test1
Found the -d option
Parameter 1: test2
Parameter 2: test3 分析 OPTIND 的变化。
# 观察 OPTIND 的变化
#!/bin/bash
while getopts :ab:cd opt ; docase $opt ina) echo Found the -a option; a_OPTIND:$OPTIND ;;b) echo Found the -b option , with value $OPTARG; b_OPTIND:$OPTIND ;;c) echo Found the -c option; c_OPTIND:$OPTIND ;;d) echo Found the -d option; d_OPTIND:$OPTIND ;;*) echo Unknown option: $opt ; e_OPTIND:$OPTIND ;;esac
done
#
echo OPTIND:$OPTIND
shift $[ $OPTIND - 1 ]echo
count1
for param in $
doecho Parameter $count: $paramcount$[ $count 1 ]
done# ./test1.sh -ad -c test2 test3 test4
Found the -a option; a_OPTIND:1 # 下个参数为d索引位置为1
Found the -d option; d_OPTIND:2 # 下个参数为c索引位置为2
Found the -c option; c_OPTIND:3 # 下个参数为test2索引位置为3
OPTIND:3Parameter 1: test2
Parameter 2: test3
# ./test1.sh -adc test2 test3 test4
Found the -a option; a_OPTIND:1 # 下个参数为d索引位置为1
Found the -d option; d_OPTIND:1 # 下个参数为c索引位置为1
Found the -c option; c_OPTIND:2 # 下个参数为test2索引位置为2
OPTIND:2Parameter 1: test2
Parameter 2: test3用户输入 read
接收输入
read命令从标准输入键盘或另一个文件描述符中接受输入。在收到输入后read命令会将数据放进一个变量
echo -n Enter your name: # -n 去除字符串末尾换行符
read name # 把用户输入保存到name变量
echo Hello $name# -p 可直接在rend命令行指定提示信息
read -p Please enter your age: age 也可以指定多个变量输入的每个 数据值都会分配给变量列表中的下一个变量如果变量数量不够剩下的数据就全部分配给最后一个变量。
read -p Enter: A B
echo $A, $B
# 执行1
Enter: a b
a, b...
# 执行2
Enter: a
a, …
# 执行3
Enter: a b c d
a, b c d 在read命令行中不指定变量。read命令会将它收到的任何数据都放进特殊环境变量REPLY中。
read -p Enter your name:
echo Hello $REPLY, welcome to my program.# 执行
Enter your name: Christine
Hello Christine, welcome to my program.等待时长
-t 参数指定 read 命令等待输入的秒数当计时结束时read命令返回一个非零退出状态。可以使用结构化语句来处理。
# 5秒内没有输入则停止等待
if read -t 5 -p Please enter your name: name ; then echo Hello $name, welcome to my script
else echo echo Sorry, too slow!
fi输入字数限制
-n 参数设置 read 命令计数输入的字符。当输入的字符数目达到预定数目时自动退出并将输入的数据赋值给变量。 如 -n 后接数值 1表示 read 命令只要接收到一个字符就退出。只要按下一个字符进行回答read 命令立即接受输入并将其传给变量无需按回车键。
read -n1 -p Do you want to continue [Y/N]? answer
case $answer in
Y | y)echo fine ,continue;;
N | n)echo ok,good bye;;
*)echo error choice;;
esac隐藏终端显示
-s 选项能够使 read 命令中输入的数据不显示在命令终端上实际上数据是显示的只是 read 命令将文本颜色设置成与背景相同的颜色。
read -s -p Enter your password: pass
echo
echo Your password $pass按行读取文件 read line
每次调用 read 命令都会读取文件中的 “一行” 文本。当文件没有可读的行时read 命令将以非零状态退出。 最常见的方法是对文件使用cat命令将结果通过管道直接传给含有read命令的while命令
# cat 命令的输出作为read命令的输入,read读到的值放在line中
cat test.txt | while read line
doecho $line
done while循环会持续通过read命令处理文件中的行直到read命令以非零退出状态码退出。
输出/输入重定向
标准文件描述符
Linux系统将每个对象当作文件处理。这包括输入和输出进程。Linux用文件描述符filedescriptor来标识每个文件对象。文件描述符是一个非负整数可以唯一标识会话中打开的文件。每个进程一次最多可以有九个文件描述符。出于特殊目的bash shell保留了前三个文件描述符0、1和2。
Linux的标准文件描述符
文件描述符缩写描述0STDIN标准输入键盘输入1STDOUT标准输出显示器输出2STDERR标准错误显示器输出
以上文件描述符的输出输入都可以通过重定向强制改变。
STDIN
STDIN文件描述符代表shell的标准输入。对终端界面来说标准输入是键盘。shell从STDIN文件描述符对应的键盘获得输入在用户输入时处理每个字符
在使用输入重定向符号时Linux会用重定向指定的文件来替换标准输入文件描述符。 它会读取文件并提取数据就如同它是键盘上键入的。
$ cat testfile.txtSTDOUT
STDOUT文件描述符代表shell的标准输出。在终端界面上标准输出就是终端显示器。shell的所有输出包括shell中运行的程序和脚本会被定向到标准输出中也就是显示器。 默认情况下大多数bash命令会将输出导向STDOUT文件描述符。不过可以用输出重定向来改变。
$ ls -l test2 # 输出重定向符号
$ cat test2
total 20
-rw-rw-r-- 1 rich rich 53 2014-10-16 11:30 test
-rw-rw-r-- 1 rich rich 0 2014-10-16 11:32 test2 通过输出重定向符号通常会显示到显示器的所有输出会被shell重定向到指定文件。 也可以将数据追加到某个文件。这可以用 符号来完成。
$ who test2
$ cat test2
total 20
-rw-rw-r-- 1 rich rich 53 2014-10-16 11:30 test
-rw-rw-r-- 1 rich rich 0 2014-10-16 11:32 test2
rich pts/0 2014-10-17 15:34 (192.168.1.2) # 追加的内容 当命令生成错误消息时shell并未将错误消息重定向到输出文件。
# 执行错误后test3文件创建成功了只是里面是空的
$ ls -al badfile test3
ls: cannot access badfile: No such file or directory
$ cat test3
$ shell对于错误消息的处理是跟普通输出分开的。如果出现了错误信息这些信息是不会出现在日志文件中的。
STDERR
STDERR文件描述符代表shell的标准错误输出。运行的程序和脚本出错时生成的错误消息都会发送到STDERR。 默认情况下STDERR文件描述符会和STDOUT文件描述符指向同样的地方终端显示。
重定向错误
只重定向错误
# 将错误信息重定向到test4文件中
$ ls -al badfile 2 test4
$ cat test4
ls: cannot access badfile: No such file or directory 可以看到STDERR文件描述符被设成2。可以选择只重定向错误消息将该文件描述符值放在重定向符号前注意之间不能有空格2。 用这种方法shell会只重定向错误消息而非普通数据。这里是另一个将STDOUT和STDERR消息混杂在同一输出中的例子
$ ls -al test badtest test2 2 test5
# ls命令的正常STDOUT输出仍然会发送到默认的STDOUT文件描述符也就是显示器
-rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2 $ cat test5 # 错误信息被重定向导test5中
ls: cannot access test: No such file or directory
ls: cannot access badtest: No such file or directory 重定向错误和数据
STDERR 和 STDOUT 分别重定向到不同文件。
$ ls -al test test2 2 test6 1 test7
$ cat test6
ls: cannot access test: No such file or directory $ cat test7
-rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2 也可以将 STDERR 和 STDOUT 的输出重定向到同一个文件。为此bash shell 提供了特殊的重定向符号
$ ls -al test test2 test3 badtest test7
$ cat test7
ls: cannot access test: No such file or directory
ls: cannot access badtest: No such file or directory
-rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2
-rw-rw-r-- 1 rich rich 0 2014-10-16 11:33 test3 当使用 符时命令生成的所有输出都会发送到同一位置包括数据和错误。 注意为了避免错误信息散落在输出文件中相较于标准输出bash shell自动赋予了错误消息更高的优先级。 在脚本中-输出重定向
临时重定向
如果有意在脚本中生成错误消息可以将单独的一行输出重定向到STDERR。你所需要做的是使用输出重定向符来将输出信息重定向到STDERR文件描述符。在重定向到文件描述符时必须在文件描述符数字之前加一个
$ cat test8
#!/bin/bash
echo This is an error 2 # 把输出重定向STDERR文件描述符2
echo This is normal output # 直接执行看不出什么
$ ./test8
This is an error
This is normal output 默认情况下Linux会将STDERR导向STDOUT。但是如果在运行脚本时重定向了STDERR脚本中所有导向STDERR的文本都会被重定向。
$ ./test8 2 test9
This is normal output$ cat test9
This is an error永久重定向
如果脚本中有大量数据需要重定向那重定向每个echo语句就会很烦琐。exec 命令可以定义某段代码的输出重定向。
$ cat test11
#!/bin/bash
# 这里以下的STDERR重定向到了testerror文件
exec 2testerror
echo This is the start of the script
echo now redirecting all output to another location
# 这里以下的STDOUT重定向到了testout文件(之前的代码还是正常标准输出)
exec 1testout
echo This output should go to the testout file
echo but this should go to the testerror file 2 $ ./test11
This is the start of the script
now redirecting all output to another location $ cat testout
This output should go to the testout file $ cat testerror
but this should go to the testerror file在脚本中-输入重定向
可以使用与脚本中重定向STDOUT和STDERR相同的方法来将STDIN从键盘重定向到其他位置。exec命令允许你将STDIN重定向到Linux系统上的文件中exec 0 testfile
$ cat testfile
AAA
BBB
CCC$ cat test.sh
exec 0 testfile
count1
while read line ; doecho Line #$count: $linecount$[ $count 1 ]
done$ ./test.sh
Line #1: AAA
Line #2: BBB
Line #3: CCC自定义重定向
在脚本中重定向输入和输出时并不局限于这3个默认的文件描述符。在shell 中最多可以有9个打开的文件描述符。其他6个从3~8的文件描述符均可用作输入或输出重定向。可以将这些文件描述符中的任意一个分配给文件然后在脚本中使用它们。本节将介绍如何在脚本中使用其他文件描述符。
创建 输出文件描述符
可以用exec命令来给输出分配文件描述符。和标准的文件描述符一样一旦将另一个文件描述符分配给一个文件这个重定向就会一直有效直到你重新分配。这里有个在脚本中使用其他文件描述符的简单例子。
$ cat test
#!/bin/bash
# using an alternative file descriptor
exec 3testout
echo This should display on the monitor
echo and this should be stored in the file 3
echo Then this should be back on the monitor $ ./test
This should display on the monitor
Then this should be back on the monitor
# 重定向到文件描述符3的那行echo语句的输出却进入了testout文件
$ cat testout
and this should be stored in the file重定向 文件描述符
现在介绍怎么恢复已重定向的文件描述符。你可以分配另外一个文件描述符给标准文件描述符反之亦然。这意味着你可以将STDOUT的原来位置重定向到另一个文件描述符然后再利用该文件描述符重定向回STDOUT。 强调在重定向到文件描述符时必须在文件描述符数字之前加一个
$ cat test14
#!/bin/bash exec 31
exec 1test14out
echo This should store in the output file
echo along with this line.
exec 13
echo Now things should be back to normal $ ./test14
Now things should be back to normal
$ cat test14out
This should store in the output file
along with this line. 首先脚本将文件描述符3重定向到文件描述符 1 的当前位置也就是STDOUT。这意味着任何发送给文件描述符3的输出都将出现在显示器上。 第二个exec命令将STDOUT重定向到文件shell现在会将发送给STDOUT的输出直接重定向到输出文件中。但是文件描述符3仍然指向STDOUT原来的位置也就是显示器。如果此时将输出数据发送给文件描述符3它仍然会出现在显示器上尽管STDOUT已经被重定向了。 在向STDOUT现在指向一个文件发送一些输出之后脚本将STDOUT重定向到文件描述符 3 的当前位置现在仍然是显示器。这意味着现在STDOUT又指向了它原来的位置显示器。 这个方法可能有点叫人困惑但这是一种在脚本中临时重定向输出然后恢复默认输出设置的常用方法。
创建 输入文件描述符
可以用和重定向输出文件描述符同样的办法重定向输入文件描述符。在重定向到文件之前先将STDIN文件描述符保存到另外一个文件描述符然后在读取完文件之后再将STDIN恢复到它原来的位置。
$ cat test15
#!/bin/bash
# redirecting input file descriptors
exec 60
exec 0 testfile
count1
while read line
do echo Line #$count: $line count$[ $count 1 ]
done
exec 06 read -p Are you done now? answer
case $answer in
Y|y) echo Goodbye;;
N|n) echo Sorry, this is the end.;;
esac $ ./test15
Line #1: This is the first line.
Line #2: This is the second line.
Line #3: This is the third line.
Are you done now? y
Goodbye 在这个例子中文件描述符6用来保存STDIN的位置。然后脚本将STDIN重定向到一个文件。read命令的所有输入都来自重定向后的STDIN也就是输入文件。 在读取了所有行之后脚本会将STDIN重定向到文件描述符6从而将STDIN恢复到原先的位置。该脚本用了另外一个read命令来测试STDIN是否恢复正常了。这次它会等待键盘的输入。
创建 读写文件描述符
也可以打开单个文件描述符来作为输入和输出。可以用同一个文件描述符对同一个文件进行读写。 不过用这种方法时需要特别注意。由于是对同一个文件进行数据读写shell会维护一个内部指针指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。
$ cat test16
#!/bin/bash exec 3 testfile
read line 3
echo Read: $line # 此时读取完第一行内部指针在testfile第二行第一个字符开始
echo This is a test line 3 # 输入会覆盖指针之后的内容$ cat testfile
This is the first line.
This is the second line.
This is the third line. $ ./test16
Read: This is the first line. $ cat testfile
This is the first line.
This is a test line 当脚本向文件中写入数据时它会从文件指针所处的位置开始。read命令读取了第一行数据所以它使得文件指针指向了第二行数据的第一个字符。在echo语句将数据输出到文件时它会将数据放在文件指针的当前位置覆盖了该位置的已有数据。
关闭 文件描述符
如果创建了新的输入或输出文件描述符shell会在脚本退出时自动关闭它们。然而在有些情况下需要在脚本结束前手动关闭文件描述符。要关闭文件描述符将它重定向到特殊符号 -
# 关闭文件描述符3
exec 3- 有个例子来说明当尝试使用已关闭的文件描述符时会怎样
$ cat badtest
exec 3 test17file
echo This is a test line of data 3
exec 3-
echo This wont work 3 # 使用已关闭的文件描述符会报错
$ ./badtest
./badtest: 3: Bad file descriptor 在关闭文件描述符后如果在脚本中打开了同一个输出文件shell 会用一个新文件来替换已有文件。这意味着如果输出数据它就会覆盖已有文件。
$ cat test17
#!/bin/bash exec 3 test17file
echo This is a test line of data 3
exec 3-
cat test17file
exec 3 test17file
echo Thisll be bad 3 $ ./test17
This is a test line of data # cat test17file $ cat test17file
Thisll be bad 列出打开的文件描述符lsof
参数描述-p允许指定进程IDPID-d允许指定要显示的文件描述符编号
要想知道进程的当前 PID可以用特殊环境变量 $$shell会将它设为当前PID。-a 选项用来对其他两个选项的结果执行布尔AND运算。
$ /usr/sbin/lsof -a -p $$ -d 0,1,2,3,4,5,6,7COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
bash 3344 rich 0u CHR 136,0 2 /dev/pts/0
bash 3344 rich 1u CHR 136,0 2 /dev/pts/0
bash 3344 rich 2u CHR 136,0 2 /dev/pts/0列描述COMMAND正在运行的命令名的前9个字符PID进程的PIDUSER进程属主的登录名FD文件描述符号以及访问类型r代表读w代表写u代表读写TYPE文件的类型CHR代表字符型BLK代表块型DIR代表目录REG代表常规文件DEVICE设备的设备号主设备号和从设备号SIZE如果有的话表示文件的大小NODE本地文件的节点号NAME文件名
特殊文件/dev/null
null文件里什么都没有。shell输出到null文件的任何数据都不会保存。在Linux系统上null文件的标准位置是 /dev/null 。重定向到该位置的任何数据都会被丢掉 不会显示。
$ ls -al /dev/null
$ cat /dev/null
$ ls -al badfile test16 2 /dev/null
-rwxr--r-- 1 rich rich 135 Oct 29 19:57 test16* tee 命令
将输出同时发送到显示器和日志文件不用将输出重定向两次只要用特殊的tee命令就行。 tee命令相当于管道的一个T型接头。它将从STDIN过来的数据同时发往两处。一处是 STDOUT另一处是tee命令行所指定的文件名。
# 由于tee会重定向来自STDIN的数据可以用它配合管道命令来重定向命令输出
$ date | tee testfile
Sun Oct 19 18:56:21 EDT 2014
$ cat testfile
Sun Oct 19 18:56:21 EDT 2014 输出出现在了STDOUT中同时也写入了指定的文件中。默认情况下tee命令会在每次使用时覆盖输出文件内容。
# -a选项可将数据追加到文件中
$ date | tee -a testfile 利用这个方法既能将数据保存在文件中也能将数据显示在屏幕上
$ cat test22
#!/bin/bash
# using the tee command for logging
tempfiletest22file
echo This is the start of the test | tee $tempfile
echo This is the second line of the test | tee -a $tempfile
echo This is the end of the test | tee -a $tempfile $ ./test22
This is the start of the test
This is the second line of the test
This is the end of the test $ cat test22file
This is the start of the test
This is the second line of the test
This is the end of the test
实例
文件重定向常见于脚本需要读入文件和输出文件时。这个样例脚本两件事都做了。它读取.csv 格式的数据文件输出SQL INSERT语句来将数据插入数据库。 shell脚本使用命令行参数指定待读取的.csv文件。.csv格式用于从电子表格中导出数据所以你可以把数据库数据放入电子表格中把电子表格保存成.csv格式读取文件然后创建INSERT 语句将数据插入MySQL数据库。
$ cat members.csv
Blum,Richard,123 Main St.,Chicago,IL,60601
Blum,Barbara,123 Main St.,Chicago,IL,60601
Bresnahan,Christine,456 Oak Ave.,Columbus,OH,43201
Bresnahan,Timothy,456 Oak Ave.,Columbus,OH,43201 $cat test23
#!/bin/bash
outfilemembers.sql
IFS,
# read 获取输入 members.csv
while read lname fname address city state zip ; do cat $outfile EOFINSERT INTO members (lname,fname,address,city,state,zip) VALUES
($lname, $fname, $address, $city, $state, $zip);
EOF
done ${1}
#done $1 done ${1} 当运行程序test23时$1代表第一个命令行参数。它指明了待读取数据的文件。read语句会使用IFS字符解析读入的文本我们在这里将IFS指定为逗号。
**cat o u t f i l e E O F ∗ ∗ 这条语句中包含一个输出追加重定向双大于号和一个输入追加重定向双小于号。输出重定向将 c a t 命令的输出追加到由 outfile EOF** 这条语句中包含一个输出追加重定向双大于号和一个输入追加重定向双小于号。输出重定向将cat命令的输出追加到由 outfileEOF∗∗这条语句中包含一个输出追加重定向双大于号和一个输入追加重定向双小于号。输出重定向将cat命令的输出追加到由outfile变量指定的文件中。cat命令的输入不再取自标准输入而是被重定向到脚本中存储的数据。EOF符号标记了追加到文件中的数据的起止。
INSERT INTO 语句其中的数据会由变量来替换变量中内容则是由read语句存入的。所以基本上while循环一次读取一行数据将这些值放入INSERT语句模板中然后将结果输出到输出文件中。
$ ./test23 members.csv$ cat members.sql INSERT INTO members (lname,fname,address,city,state,zip) VALUES (Blum, Richard, 123 Main St., Chicago, IL, 60601); INSERT INTO members (lname,fname,address,city,state,zip) VALUES (Blum, Barbara, 123 Main St., Chicago, IL, 60601); INSERT INTO members (lname,fname,address,city,state,zip) VALUES (Bresnahan, Christine, 456 Oak Ave., Columbus, OH, 43201); INSERT INTO members (lname,fname,address,city,state,zip) VALUES (Bresnahan, Timothy, 456 Oak Ave., Columbus, OH, 43201);创建临时文件 mktemp
Linux系统有特殊的目录专供临时文件使用。Linux使用/tmp目录来存放不需要永久保留的文件。大多数Linux发行版配置了系统在启动时自动删除/tmp目录的所有文件。系统上的任何用户账户都有权限在读写/tmp目录中的文件。 mktemp 命令可以在/tmp目录中创建一个唯一的临时文件。shell会创建这个文件但不用默认的umask值。它会将文件的读和写权限分配给文件的属主并将你设成文件的属主。一旦创建了文件你就在脚本中有了完整的读写权限但其他人没法访问它当然root用户除外。
创建本地临时文件
默认情况下mktemp会在本地目录中创建一个文件。要用mktemp命令在本地目录中创建一个临时文件只要指定一个文件名模板就行了。模板可以包含任意文本文件名在文件名末尾加上6个大写的X即可。 mktemp命令会用6个字符码替换这6个X从而保证文件名在目录中是唯一的。你可以创建多个临时文件它可以保证每个文件都是唯一的。
$ mktemp testing.XXXXXX
testing.1DRLuV 在脚本中使用mktemp命令时将文件名保存到变量中这样就能在后面的脚本中引用了
#!/bin/bash
tempfile$(mktemp test19.XXXXXX)
exec 3$tempfile
echo This script writes to temp file $tempfile
echo This is the first line 3在 /tmp 目录创建临时文件
-t 选项会强制mktemp命令来在系统的临时目录来创建该文件并返回用来创建临时文件的全路径。
$ mktemp -t test.XXXXXX
/tmp/test.xG3374 由于mktemp命令返回了绝对路径可以在Linux系统上的任何目录下引用该临时文件
$ cat test
#!/bin/bash
# creating a temp file in /tmp
tempfile$(mktemp -t tmp.XXXXXX)
echo This is a test file. $tempfile
echo This is the second line of the test. $tempfile
echo The temp file is located at: $tempfile
cat $tempfile
rm -f $tempfile$ ./test
The temp file is located at: /tmp/tmp.Ma3390
This is a test file.
This is the second line of the test. 创建临时目录
-d 选项告诉mktemp命令来创建一个临时目录而不是临时文件。
$ cat test21
#!/bin/bash
# using a temporary directory
tempdir$(mktemp -d dir.XXXXXX)
cd $tempdir
tempfile1$(mktemp temp.XXXXXX)
tempfile2$(mktemp temp.XXXXXX)
exec 7 $tempfile1
exec 8 $tempfile2 控制脚本
处理信号类似捕获异常
Linux 信号
Linux系统和应用程序可以生成超过30个信号Linux编程时会遇到的最常见的Linux系统信号signals如下
信号值描述1SIGHUP挂起进程2SIGINT终止进程3SIGQUIT停止进程9SIGKILL无条件终止进程15SIGTERM尽可能终止进程17SIGSTOP无条件停止进程但不是终止进程18SIGTSTP停止或暂停进程但不终止进程19SIGCONT继续运行停止的进程
默认情况下bash shell会忽略收到的任何SIGQUIT (3)和SIGTERM (5)信号正因为这样交互式shell才不会被意外终止。但是bash shell会处理收到的SIGHUP (1)和SIGINT (2)信号。 如果bash shell收到了SIGHUP信号比如当你要离开一个交互式shell它就会退出。但在退出之前它会将SIGHUP信号传给所有由该shell所启动的进程包括正在运行的shell脚本。 通过SIGINT信号可以中断shell。Linux内核会停止为shell分配CPU处理时间。这种情况发 生时shell会将SIGINT信号传给所有由它所启动的进程以此告知出现的状况。 shell会将这些信号传给shell脚本程序来处理。而shell脚本的默认行为是忽略这些信号。它们可能会不利于脚本的运行。要避免这种情况可以脚本中加入识别信号的代码并执行命令来处理信号。
生成信号
bash shell允许用键盘上的组合键生成两种基本的Linux信号。这个特性在需要停止或暂停失控程序时非常方便。
中断进程 CtrlC 组合键会生成SIGINT信号并将其发送给当前在shell中运行的所有进程。
# 在超时前按下CtrlC组合键就可以提前终止sleep命令
$ sleep 100
^C
$ 暂停进程 CtrlZ 组合键会生成一个SIGTSTP信号停止shell中运行的任何进程。 停止stopping进程跟终止terminating进程不同停止进程会让程序继续保留在内存中并能从上次停止的位置 继续运行。
# 停止sleep命令
$ sleep 100
^Z
[1] Stopped sleep 100
$ [1] 方括号中的数字是shell分配的作业号job number。shell将shell中运行的每个进程称为作业并为每个作业分配唯一的作业号。它会给第一个作业分配作业号1第二个作业号2以此类推。 如果shell会话中有一个已停止的作业在退出shell时bash会提醒
$ sleep 100
^Z
[1] Stopped sleep 100
$ exit
exit
There are stopped jobs. # 可以用ps命令来查看已停止的作业
$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 501 2431 2430 0 80 0 - 27118 wait pts/0 00:00:00 bash
0 T 501 2456 2431 0 80 0 - 25227 signal pts/0 00:00:00 sleep
0 R 501 2458 2431 0 80 0 - 27034 - pts/0 00:00:00 ps
# 在S列中进程状态ps命令将已停止作业的状态为显示为T。这说明命令要么被跟踪要么被停止了 如果在有已停止作业存在的情况下要退出 shell 只要再输入一遍exit命令就行了shell会退出并终止已停止作业。或者利用kill命令终止已停止作业PID。
捕获信号
trap命令允许指定 shell 脚本要监看并从shell中拦截的Linux信号。如果脚本收到了trap命令中列出的信号该信号不再 由shell处理而是交由本地处理。
trap commands signals 在trap命令行上只需列出想要shell执行的命令以及一组用空格分开的待捕 获的信号。可以用数值或Linux信号名来指定信号。 下面例子展示了如何使用trap命令来忽略 SIGINT 信号并控制脚本的行为。
$ cat test1.sh
#!/bin/bash
# 每次 Ctrl-CSIGINT 终止进程时的时候提示这句话
trap echo Sorry! I have trapped Ctrl-C SIGINT
#
echo This is a test script
#
count1
while [ $count -le 5 ]
do echo Loop #$count sleep 1 count$[ $count 1 ]
done
#
echo This is the end of the test script 本例中用到的trap命令会在每次检测到SIGINT信号时显示一行简单的文本消息。捕获这些信号会阻止用户用bash shell组合键CtrlC来停止程序。
$ ./test1.sh
This is a test script
Loop #1
Loop #2
Loop #3
^C Sorry! I have trapped Ctrl-C
Loop #4
^C Sorry! I have trapped Ctrl-C
Loop #5
This is the end of the test script 每次使用CtrlC组合键脚本都会执行trap命令中指定的echo语句而不是处理该信号停止该脚本。
捕获脚本退出
除了在shell脚本中捕获信号也可以在shell脚本退出时进行捕获。这是在shell完成任务时执行命令的一种简便方法。 要捕获shell脚本的退出只要在trap命令后加上EXIT信号就行。
$ cat test2.sh
#!/bin/bash
trap echo Goodbye... EXIT
count1
while [ $count -le 5 ]
do echo Loop #$count sleep 1 count$[ $count 1 ]
done $ ./test2.sh
Loop #1
Loop #2
Loop #3
Loop #4
Loop #5
Goodbye...
# 当脚本运行到正常的退出位置时捕获就被触发了shell会执行在trap命令行指定的命令。如果提前退出脚本同样能够捕获到EXIT
$ ./test2.sh
Loop #1
Loop #2
Loop #3
^CGoodbye... 因为SIGINT信号并没有出现在trap命令的捕获列表中当按下CtrlC组合键发送SIGINT信号时脚本就退出了。但在脚本退出前捕获到了EXIT于是shell执行了trap命令。
修改或移除捕获
要想在脚本中的不同位置进行不同的捕获处理只需重新使用带有新选项的trap命令。
trap echo Sorry... Ctrl-C is trapped. SIGINT
...
# 修改只需要再次调用就行
trap echo I modified the trap! SIGINT
...删除捕获设置
删除已设置好的捕获。只需要在trap命令与希望恢复默认行为的信号列表之间加上两个破折号就行了。
trap -- SIGINT 移除信号捕获后脚本按照默认行为来处理SIGINT信号。 后台运行脚本
只要在执行命令后加个 符就行
$ ./xxx.sh
[1] 3231 回显的内容中方括号中的数字是shell分配给后台进程的作业号。下一个数是Linux系统分配给进程的进程 IDPID。Linux系统上运行的每个进程都必须有一个唯一的 PID。
注意当后台进程运行时它仍然会使用终端显示器来显示STDOUT和STDERR消息。最好是将后台运行的脚本的STDOUT和STDERR进行重定向避免杂乱的输出。
运行多个后台作业
$ ./test6.sh
[1] 3568
$ This is Test Script #1
$ ./test7.sh
[2] 3570
$ This is Test Script #2
$ ./test8.sh
[3] 3573
$ And...another Test script 每次启动新作业时Linux系统都会为其分配一个新的作业号和PID。 通过ps命令可以看到 所有脚本处于运行状态。
$ psPID TTY TIME CMD 2431 pts/0 00:00:00 bash 3568 pts/0 00:00:00 test6.sh 3570 pts/0 00:00:00 test7.sh 3573 pts/0 00:00:00 test8.sh 3574 pts/0 00:00:00 sleep 3575 pts/0 00:00:00 sleep 3577 pts/0 00:00:00 sleep 3578 pts/0 00:00:00 sleep 3579 pts/0 00:00:00 ps注意 在终端会话中使用后台进程时每一个后台进程都和终端会话pts/0终端联系在一起。如果终端会话退出那么后台进程也会随之退出。 在非控制台下运行脚本
如果希望运行在后台模式的脚本在登出控制台后能够继续运行到结束即使退出了终端会话。这可以用nohup命令来实现。 nohup 命令运行了另外一个命令来阻断所有发送给该进程的SIGHUP信号。这会在退出终端会话时阻止进程退出。
$ nohup ./test1.sh
[1] 3856
$ nohup: ignoring input and appending output to nohup.out 由于nohup命令会解除终端与进程的关联进程也就不再同STDOUT和STDERR联系在一起。为了保存该命令产生的输出nohup命令会自动将STDOUT和STDERR的消息重定向到一个名为 nohup.out 的文件中。 nohup.out 文件包含了通常会发送到终端显示器上的所有输出。 说明 如果使用nohup运行了另一个命令该命令的输出会被追加到已有的nohup.out文件中。当运行位于同一个目录中的多个命令时一定要当心因为所有的输出都会被发送到同一个nohup.out文件中。 作业控制
启动、停止、终止以及恢复作业的这些功能统称为作业控制。通过作业控制就能完全控制shell环境中所有进程的运行方式了。
查看作业
jobs 命令可以查看shell当前正在处理的作业。
$ cat test10.sh
#!/bin/bash
echo Script Process ID: $$
count1
while [ $count -le 10 ]
do echo Loop #$count sleep 10 count$[ $count 1 ]
done
echo End of script... 脚本用**$$**变量来显示Linux系统分配给该脚本的PID然后进入循环每次迭代都休眠10秒。可以从命令行中启动脚本然后使用CtrlZ组合键来停止脚本。
$ ./test10.sh
Script Process ID: 1897
Loop #1
Loop #2
^Z
[1] Stopped ./test10.sh# 然后利用 将另外一个作业作为后台进程启动
$ ./test10.sh test10.out
[2] 1917 jobs命令会显示这两个已停止/运行中的作业以及它们的作业号和作业中使用的命令。
$ jobs -l
[1] 1897 Stopped ./test10.sh
[2]- 1917 Running ./test10.sh test10.out 参数描述-l列出进程的PID以及作业号-n只列出上次shell发出的通知后改变了状态的作业-p只列出作业的PID-r只列出运行中的作业-s只列出已停止的作业
带加号的作业会被当做默认作业。在使用作业控制命令时如果未在命令行指定任何作业号该作业会被当成作业控制命令的操作对象。
当前的默认作业完成处理后带减号的作业成为下一个默认作业。任何时候都只有一个带加号的作业和一个带减号的作业不管shell中有多少个正在运行的作业。
重启作业
在bash作业控制中可以将已停止的作业作为后台进程或前台进程重启。
# 以后台模式重启作业
$ bg [作业号]# 以前台模式重启作业
$ fg [作业号]调度优先级
在多任务操作系统中Linux就是内核负责将CPU时间分配给系统上运行的每个进程。调度优先级scheduling priority是内核分配给进程的CPU时间相对于其他进程。在Linux系统 中由shell启动的所有进程的调度优先级默认都是相同的。
调度优先级是个整数值从 -20最高优先级到 19最低优先级。默认情况下bash shell 以优先级0来启动所有进程。
nice 命令
可以设置命令启动时的调度优先级。注意必须将nice命令和要启动的命令放在同一行中。
$ nice -n 10 ./test4.sh test4.out
[1] 4973 # 查看进程信息ni优先级
$ ps -p 4973 -o pid,ppid,ni,cmdPID PPID NI CMD 4973 4721 10 /bin/bash ./test4.sh 注意 只能通过nice降低进程的优先级root 用户或 sudo权限不受限制 renice 命令
可以改变系统上已运行命令的优先级指定运行进程的PID。
$ ./test11.sh
[1] 5055 $ ps -p 5055 -o pid,ppid,ni,cmdPID PPID NI CMD 5055 4721 0 /bin/bash ./test11.sh $ renice -n 10 -p 5055
5055: old priority 0, new priority 10 $ ps -p 5055 -o pid,ppid,ni,cmdPID PPID NI CMD 5055 4721 10 /bin/bash ./test11.sh注意 renice 只能对属于你的进程执行 只能通过renice降低进程的优先级root 用户或sudo权限不受限制 定时执行
at 在预设时间执行 crontab定期执行执行错过时间点的任务不执行 anacron错过的任务也能执行
函数 function
创建
# 第一种
function name { commands
} # 第二种
name() { commands
} 调用
$ cat test1
#!/bin/bash function func1 { echo This is an example of a function
}# 调用
func1返回值
bash shell会把函数当作一个小型脚本运行结束时会返回一个退出状态码。有3种不同的方法来为函数生成退出状态码。
默认退出状态码
默认情况下函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后可以用标准变量 $? 来确定函数的退出状态码。
$ cat test4
#!/bin/bash func1() { echo trying to display a non-existent file ls -l badfile
}
func1
echo The exit status is: $? $ ./test4
trying to display a non-existent file
ls: badfile: No such file or directory
The exit status is: 1
# 函数的退出状态码是1这是因为函数中的最后一条命令(ls)没有成功运行。但无法知道函数中其他命令中是否成功运行。# 看下面的例子
$ cat test4b
#!/bin/bash func1() { ls -l badfile echo This was a test of a bad command
}
func1
echo The exit status is: $? $ ./test4b
ls: badfile: No such file or directory
This was a test of a bad command
The exit status is: 0
# 由于函数最后一条语句echo运行成功该函数的退出状态码就是0尽管其中有一条命令并没有正常运行。所以使用函数的默认退出状态码是存在问题的。return 命令
return命令退出函数并返回特定的退出状态码。return命令允许指定一个整数值来定义函数的退出状态码。
$ cat test5
#!/bin/bash
function dbl { echo doubling the value return 66
}
dbl
echo The new value is $? 函数一结束就取返回值如果在用 ? 变量提取函数返回值之前执行了其他命令函数的返回值就会丢失。 ?变量提取函数返回值之前执行了其他命令函数的返回值就会丢失。 ?变量提取函数返回值之前执行了其他命令函数的返回值就会丢失。? 变量会返回执行的最后一条命令的退出状态码 退出状态码必须是0~255任何大于256的值都会产生一个错误值。 接收返回值
正如可以将命令的输出保存到shell变量中一样也可以对函数的输出采用同样的处理办法。
# 将fun01函数的输出返回值赋给$result变量
result$(fun01)在函数中使用变量
函数传参
函数可以使用标准的参数环境变量来表示命令行上传给函数的参数。例如函数名会在 $0 变量中定义函数命令行上的任何参数都会通过$1、$2等定义。也可以用特殊变量$#来判断传给函数的参数数目。然后函数可以用参数环境变量来获得参数值。 脚本中指定函数时必须将参数和函数放在同一行格式如下 fun1 $value1 $value2 n1 n2 …
#!/bin/bash
# 函数传参
function addem {echo $[ $1 $2 ]
}
echo -n Adding:
value$(addem 10 15)
echo $value# 执行
$./test.py
Adding: 25 由于函数使用特殊参数环境变量作为自己的参数值因此它无法直接获取脚本在命令行中的参数值。
function addem { echo $[ $1 $2 ]
}
echo -n Adding :
value$(addem)
echo $value$ ./test 10 15
Adding :
./test.py: line 3: : syntax error: operand expected (error token is ) 尽管函数也使用了$1和$2变量但它们和脚本主体中的$1和$2变量并不相同。要在函数中使用这些值必须在调用函数时手动将它们传过去。
function addem { echo $[ $1 $2 ]
}
echo -n Adding :
value$(addem $1 $2) # 通过将$1和$2变量传给函数它们就能跟其他变量一样供函数使用了
echo $value$ ./test 10 15
Adding : 25全局变量
全局变量在shell脚本中任何地方都有效。默认情况下在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问。
function func1 {temp$[ $temp 10 ]
}
temp4
echo old_temp:$temp
func1
echo new_temp:$temp# 执行后
old_temp:4
new_temp:14 A变量在函数外定义并被赋值。当函数被调用时该变量及其值在函数中都依然有效。如果变量在函数内被改变了那么在函数外再引用该变量时A变量是改变后的值。 这样可能存在一些问题尤其是如果想在不同的shell脚本中使用函数的话。
局部变量
声明局部变量local temp local关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字的变量那么shell将会保持这两个变量互不影响。
function func1 {local temp$[ $temp 10 ] # local 声明局部变量
}
temp4
echo old_temp:$temp
func1
echo new_temp:$temp# 执行后
old_temp:4
new_temp:4 现在在func1函数中使用 t e m p 变量时并不会影响在脚本主体中赋给 temp变量时并不会影响在脚本主体中赋给 temp变量时并不会影响在脚本主体中赋给temp变量的值。
数组参数
将数组变量当作单个参数传递的话函数只会取数组变量的第一个值。
$ cat badtest3
#!/bin/bash
function testit { echo The parameters are: $
} myarray(1 2 3 4 5)
echo The original array is: ${myarray[*]}
testit $myarray $ ./badtest3
The original array is: 1 2 3 4 5
The parameters are: 1 要解决这个问题必须将数组变量的值分解成单个的值然后将这些值作为函数参数使用。
$ cat test10
#!/bin/bash function testit {echo The parameters are: $local newarray$ echo The new array value is: ${newarray[*]}
}
myarray(1 2 3 4 5)
echo The original array is ${myarray[*]}
testit ${myarray[*]} $ ./test10
The original array is 1 2 3 4 5
The parameters are: 1 2 3 4 5
The new array value is: 1 2 3 4 5# 案例数组求和
$ cat test11
function addarray { local sum0 local newarray$for value in ${newarray[*]} ; do sum$[ $sum $value ] done echo $sum
}
myarray(1 2 3 4 5)
echo The original array is: ${myarray[*]}
arg1$(echo ${myarray[*]})
result$(addarray $arg1)
echo The result is $result $ ./test11
The original array is: 1 2 3 4 5
The result is 15 递归
函数可以调用自己来得到结果。通常递归函数都有一个最终可以迭代到的基准值。 递归算法的经典例子是计算阶乘x! x * (x-1)!
$ cat test13
#!/bin/bash
function factorial { if [ $1 -eq 1 ] ; then echo 1 else local temp$[ $1 - 1 ] local result$(factorial $temp) echo $[ $result * $1 ] fi
}
read -p Enter value: value
result$(factorial $value)
echo The factorial of $value is: $result $ ./test13
Enter value: 5
The factorial of 5 is: 120 创建函数库类似模块
创建函数库文件包含一些函数然后在其他脚本中通过source命令引用该库文件。
$ cat myfuncs
# my script functions
function addem { echo $[ $1 $2 ]
} source 命令会在当前shell上下文中执行命令而不是创建一个新shell。可以用source命令来在shell脚本中运行库文件脚本。
source命令有个快捷的别名称作点操作符. ./myfuncs
$ cat test14
#!/bin/bash # 使用相应路径访问该文件
. ./myfuncsvalue110
value25
result1$(addem $value1 $value2)
echo The result of adding them is: $result1 $ ./test14
The result of adding them is: 15 在命令行上使用函数
在命令行上创建函数
因为shell会解释用户输入的命令所以可以在命令行上直接定义一个函数
# 单行方式定义函数
$ function divem { echo $[ $1 / $2 ]; }
$ divem 100 5
20# 多行方式来定义函数
$ function multem { echo $[ $1 * $2 ] }
$ multem 2 5
10 在命令行上创建函数时。如果给函数起了个跟内建命令或另一个命令相同的名字函数将会覆盖原来的命令。 在.bashrc 文件中定义函数
在命令行上直接定义shell函数的明显缺点是退出shell时函数就消失了。 持久生效的话将函数定义在主目录下的 .bashrc 文件中这个文件在每次启动一个新shell的时候都会由shell重新载入。
$ cat .bashrc
# .bashrc
# Source global definitions
if [ -r /etc/bashrc ]; then . /etc/bashrc
fi
# 直接定义函数
function addem { echo $[ $1 $2 ]
}
# 读取函数文件
. /home/xxx/myfuncs该文件中的函数会在下次启动新bash shell时生效。随后就能在系统上任意地方使用这个函数了。 shell还会将定义好的函数传给子shell进程所以任何shell脚本中都能使用甚至都不用对库文件使用source。 GNU shtool 脚本函数库
GNU shtool shell脚本函数库提供了一些简单的shell脚本函数可以用来完成日常的shell功能例如处理临时文件和目录或者格式化输出显示。 使用教程https://blog.csdn.net/Qiu_SaMa/article/details/120607394
$ tar -zxvf shtool-2.0.8.tar.gz
$ ./confifgure
$ make
# 使用
# shtool [options] [function [options] [args]] 图形化脚本编程
创建菜单布局
可以用这个模板创建任何shell脚本菜单界面。它提供了一种跟用户交互的简单途径。 echo 命令中包含制表符和换行符必须用 -e 选项 -en 选项会去掉末尾的换行符 read -n 选项来限制只读取一个字符 $ cat menu1
#!/bin/bash function diskspace { clear # 清屏df -k
}
function whoseon { clear who
}
function memusage { clear cat /proc/meminfo
} # 菜单展示
function menu { clear echo echo -e \t\t\tSys Admin Menu\n echo -e \t1. Display disk spaceecho -e \t2. Display logged on users echo -e \t3. Display memory usage echo -e \t0. Exit program\n\n echo -en \t\tEnter option: read -n 1 option
} # 菜单逻辑
while [ 1 ]
do menu case $option in 0) break ;; 1) diskspace ;; 2) whoseon ;; 3) memusage ;; *) clear echo Sorry, wrong selection;; esac echo -en \n\n\t\t\tHit any key to continue read -n 1 line
done
clear Sys Admin Menu1. Display disk space2. Display logged on users3. Display memory usage0. Exit programEnter option:select 工具
创建文本菜单的一半工夫都花在了建立菜单布局和获取用户输入。select 命令只需要一条命令就可以创建出菜单然后获取输入的答案并自动处理。 list 参数是由空格分隔的文本选项列表这些列表构成了整个菜单。select命令会将每个列表项显示成一个带编号的选项然后为选项显示一个由PS3环境变量定义的特殊提示符。 PS3定制shell脚本的select提示默认提示 # $ cat smenu1
#!/bin/bash function diskspace { clear df -k
}
function whoseon { clear who
}
function memusage { clear cat /proc/meminfo
} PS3Enter option:
select option in Display disk space Display logged on users Display memory usage Exit program
do case $option in Exit program) break ;; Display disk space) diskspace ;; Display logged on users) whoseon ;; Display memory usage) memusage ;; *) clear echo Sorry, wrong selection;; esac
done
clear$ ./smenu1
1) Display disk space 3) Display memory usage
2) Display logged on users 4) Exit program
Enter option: 在使用select命令时记住存储在变量中的结果值是整个文本字符串而不是跟菜单选项相关联的数字。文本字符串值才是你要在case语句中进行比较的内容。 制作窗口 dialog
dialog 能够用ANSI转义控制字符在文本环境中创建标准的窗口对话框。dialog 命令使用命令行参数来决定生成哪种窗口部件widget。部件是dialog包中窗口元素类型的术语。
dialog包支持的标准部件
部 件描 述calendar提供选择日期的日历checklist显示多个选项其中每个选项都能打开或关闭form构建一个带有标签以及文本字段可以填写内容的表单fselect提供一个文件选择窗口来浏览选择文件gauge显示完成的百分比进度条infobox显示一条消息但不用等待回应inputbox提供一个输入文本用的文本表单inputmenu提供一个可编辑的菜单menu显示可选择的一系列选项返回选项号msgbox显示一条消息并要求用户选择OK按钮pause显示一个进度条来显示暂定期间的状态passwordbox显示一个文本框但会隐藏输入的文本passwordform显示一个带标签和隐藏文本字段的表单radiolist提供一组菜单选项但只能选择其中一个tailbox用tail命令在滚动窗口中显示文件的内容tailboxbg跟tailbox一样但是在后台模式中运行textbox在滚动窗口中显示文件的内容timebox提供一个选择小时、分钟和秒数的窗口yesno提供一条带有Yes和No按钮的简单消息
要在命令行上指定某个特定的部件需使用双破折线格式
dialog --widget parameters
# widget部件名
# parameters部件窗口的大小以及部件需要的文本每个dialog部件都提供了两种形式的输出 1、STDERR 2、退出状态码
可以通过dialog命令的退出状态码来确定用户选择的按钮。可以用标准的 $? 变量来确定dialog部件中具体选择了哪个按钮。 如果部件返回了数据比如菜单选择那么dialog命令会将数据发送到STDERR。可以用标准的bash shell方法来将STDERR输出重定向到另一个文件或文件描述符中。
# 将文本框中输入的文本重定向到age.txt文件中
$ dialog --inputbox Enter your age: 10 20 2age.txt 部件案例
1、msgbox部件
msgbox 部件是对话框中最常见的类型。它会在窗口中显示一条消息直到用户单击OK按钮后才消失。
$ dialog --title Testing --msgbox This is a test 10 20 2、yesno部件
yesno 允许用户选择 yes 或 no。 它会在窗口底部生成两个按钮一个是Yes一个是No。 dialog命令的退出状态码会根据用户选择的按钮来设置。如果用户选择了No按钮退出状态码是1如果选择了Yes按钮退出状态码就是0。
$ dialog --title Please answer --yesno Is this thing on? 10 20 3、inputbox部件
inputbox部件为用户提供了一个简单的文本框区域来输入文本字符串。dialog命令会将文本字符串的值发给STDERR。必须重定向STDERR来获取用户输入。 inputbox提供了两个按钮OK和Cancel。如果选择了OK按钮命令的退出状态码就是0反之退出状态码就会是1。
[userhostname]$ dialog --inputbox Enter your age: 10 20 2age.txt
[userhostname]$ echo $?
0
[userhostname]$ cat age.txt
12[userhostname]$ 在使用cat命令显示文本文件的内容时该值后面并没有换行符。这有助于将文件内容重定向到shell脚本中的变量里以提取用户输入的字符串 4、textbox部件
textbox 部件是在窗口中显示大量信息的极佳办法。它会生成一个滚动窗口来显示由参数所指定的文件中的文本。
# 可以用方向键来左右或上下滚动/etc/passwd的内容
$ dialog --textbox /etc/passwd 15 455、menu部件
menu 部件可以创建文本菜单。只要为每个选项提供一个选择标号和文本就行了。dialog命令会将选定的菜单标号发送到STDERR可以根据需要重定向STDERR。
$ dialog --menu Sys Admin Menu 20 30 4 1 Display disk space 2 Display users 3 Display memory usage 4 Exit 2 test.txt# 参数“30”后的参数“4”定义了在窗口中一次显示的菜单项总数为4条6、fselect部件
可以用fselect部件来浏览文件的位置并选择文件。
$ dialog --title Select a file --fselect $HOME/ 10 50 2file.txt fselect 选项后的第一个参数是窗口中使用的起始目录位置。fselect部件窗口由左侧的目录列表、右侧的文件列表显示了选定目录下的所有文件和含有当前选定的文件或目录的简单文本框组成。可以手动在文本框键入文件名或者用目录和文件列表来选定使用空格键选择文件将其加入文本框中。
dialog 选项
dialog 选项可以重写对话窗口中的任意按钮标签。该特性允许创建任何需要的窗口。
除了标准部件还可以在dialog命令中定制很多不同的选项。如 -title 可以设置窗口标题。另外还有许多其他的选项可以全面定制窗口外观和操作。dialog命令中可用的选项见下表
选项描述–add-widget继续下个对话框直到按下Esc或Cancel按钮–aspect ratio指定窗口宽度和高度的宽高比–backtitle title指定显示在屏幕顶部背景上的标题–begin x y指定窗口左上角的起始位置–cancel-label label指定Cancel按钮的替代标签–clear用默认的对话背景色来清空屏幕内容–colors在对话文本中嵌入ANSI色彩编码–cr-wrap在对话文本中允许使用换行符并强制换行–create-rc file将示例配置文件的内容复制到指定的file文件中①–defaultno将yes/no对话框的默认答案设为No–default-item string设定复选列表、表单或菜单对话中的默认项–exit-label label指定Exit按钮的替代标签–extra-button在OK按钮和Cancel按钮之间显示一个额外按钮–extra-label label指定额外按钮的替代标签–help显示dialog命令的帮助信息–help-button在OK按钮和Cancel按钮后显示一个Help按钮–help-label label指定Help按钮的替代标签–help-status当选定Help按钮后在帮助信息后写入多选列表、单选列表或表单信息–ignore忽略dialog不能识别的选项–input-fd fd指定STDIN之外的另一个文件描述符–insecure在password部件中键入内容时显示星号–item-help为多选列表、单选列表或菜单中的每个标号在屏幕的底部添加一个帮助栏–keep-window不要清除屏幕上显示过的部件–max-input size指定输入的最大字符串长度。默认为2048–nocancel隐藏Cancel按钮–no-collapse不要将对话文本中的制表符转换成空格–no-kill将tailboxbg对话放到后台并禁止该进程的SIGHUP信号–no-label label为No按钮指定替代标签–no-shadow不要显示对话窗口的阴影效果–ok-label label指定OK按钮的替代标签–output-fd fd指定除STDERR之外的另一个输出文件描述符–print-maxsize将对话窗口的最大尺寸打印到输出中–print-size将每个对话窗口的大小打印到输出中–print-version将dialog的版本号打印到输出中–separate-output一次一行地输出checklist部件的结果不使用引号–separator string指定用于分隔部件输出的字符串–separate-widget string指定用于分隔部件输出的字符串–shadow在每个窗口的右下角绘制阴影–single-quoted需要时对多选列表的输出采用单引号–sleep sec在处理完对话窗口之后延迟指定的秒数–stderr将输出发送到STDERR默认行为–stdout将输出发送到STDOUT–tab-correct将制表符转换成空格–tab-len n指定一个制表符占用的空格数默认为8–timeout sec指定无用户输入时sec秒后退出并返回错误代码–title title指定对话窗口的标题–trim从对话文本中删除前导空格和换行符–visit-items修改对话窗口中制表符的停留位置使其包括选项列表–yes-label label为Yes按钮指定替代标签
–create-rcdialog命令支持运行时配置。该命令会根据配置文件模板创建一份配置文件。dialog启动时会先去检查是否设置了 DIALOGRC 环境变量该变量会保存配置文件名信息。如果未设置该变量或未找到该文件它会将 $HOME/.dialogrc作为配置文件。如果这个文件还不存在的话就尝试查找编译时指定的GLOBALRC文件也就 是/etc/dialogrc。如果这个文件也不存在的话就用编译时的默认值。
–backtitle是为脚本中的菜单创建公共标题的简便办法。如果你为每个对话窗口都指定了该选项那么它在你的应用中就会保持一致。
在脚本中应用
要在脚本中是使用dialog需要遵循两个规则 1、如果有Cancel或No按钮检查dialog命令的退出状态码 2、重定向STDERR来获得输出值
使用dialog部件来生成系统管理菜单
#!/bin/bash # 创建临时文件
temp$(mktemp -t test.XXXXXX) # 保存df和meminfo命令的输出用于textbox部件显示
temp2$(mktemp -t test2.XXXXXX) # 保存菜单选项号function diskspace { df -k $temp dialog --textbox $temp 20 60
}
function whoseon { who $temp dialog --textbox $temp 20 50
}
function memusage { cat /proc/meminfo $temp dialog --textbox $temp 20 50
}# 执行完每个函数之后脚本都会返回继续显示菜单
# 只有选择“0”或者选择“Cancel”按钮才会退出循环
while [ 1 ]
do
dialog --menu Sys Admin Menu 20 30 10 1 Display disk space 2 Display users 3 Display memory usage 0 Exit 2 $temp2
# 检查dialog命令的退出状态码选择Cancel按钮1时直接退出
if [ $? -eq 1 ]
then break
fi selection$(cat $temp2)
case $selection in
1) diskspace ;;
2) whoseon ;;
3) memusage ;;
0) break ;;
*) dialog --msgbox Sorry, invalid selection 10 30
esac
donerm -f $temp 2 /dev/null
rm -f $temp2 2 /dev/null使用图形
如果想给交互脚本加入更多的图形元素KDE 和 GNOME 桌面环境都扩展了dialog命令的思路包含了可以在各自环境下生成X Window图形化部件的命令。 kdialog和zenity包它们各自为KDE和GNOME桌面提供了图形化窗口部件。