详解shell脚本(四)—— 基础进阶

时间:2021-10-17 21:45:13

调试脚本

#! /bin/bash
function DEBUG()
{
[ "$_DEBUG"=="on" ] && $@ || :
}
for i in {1..10}
do
DEBUG echo $i
done
//调试
_DEBUG=on ./script.sh

把shebang从#! /bin/bash 改成 #!/bin/bash -xv,这样一来,不用任何其他选项就可以启用调试功能了。

子shell

子shell本身就是独立的进程。可以使用()操作符来定义一个子shell

当命令在子shell中执行时,不会对当前shell有任何影响;所有的改变仅限于子shell内。例如,当用cd命令改变子shell的当前目录时,这种变化不会反映到主shell环境中。

假设我们使用子shell或反引用的方法将命令的输出读入一个变量中,可以将它放入双引号中,以保留空格和换行符(\n)。例如:

cat text.txt
1
2
3

out=$(cat text.txt)
echo $out
1 2 3 #Lost \n spacing in 1,2,3

out="$(cat text.txt)"
echo $out
1
2
3

fork炸弹

:(){ :|:& };:

可以通过修改配置文件/etc/security/limits.conf来限制可生成的最大进程数来避开这枚炸弹。

read

下面的语句从输入中读取n个字符并存入变量variable_name:

read -n number_of_chars variable_name
read -n 2 var
echo $var
#用无回显的方式读取密码
read -s var
#显示提示信息
read -p "Enter input:" var
#在特定时限内读取输入
read -t timeout var
read -t 2 var #在2秒内将键入的字符串读入变量var
#用特定的定界符作为输入行的结束
read -d delim_char var
read -d ":" var #hello:var 被设置为 hello

运行命令直至执行成功

repeat()
{
while true
do
$@ && return
done
}

我们创建了函数repeat,它包含了一个无限while循环,该循环执行以参数形式(通过$@访问)传入函数的命令。如果命令执行成功,则返回,进而退出循环。

repeat() { while :; do $@ && return; done }
repeat() { while :; do $@ && return; sleep 30; done }
#延时
repeat wget -c http://www.example.com/software-0.1.tar.gz#假设用repeat()从Internet上下载一个暂时不可用的文件

[ condition ] && action; # 如果condition为真,则执行action;

[ condition ] || action; # 如果condition为假,则执行action。

字符分隔符和迭代器

IFS是存储定界符的环境变量。它是当前shell环境使用的默认定界字符串。考虑一种情形:我们需要迭代一个字符串或逗号分隔型数值(Comma Separated Value, CSV)中的单词。如果是前一种,则使用IFS=”.”;如果是后一种,则使用IFS=”,”。接下来看看具体的做法。

data="name,sex,rollno.location"
#我们可以使用IFS读取变量中的每一个条目
oldIFS=$IFS
IFS=","
for item in $data;
do
echo Item: $item
done

IFS=$oldIFS

IFS的默认值为空白字符(换行符、制表符或者空格)。

循环

##for
for var in list;
do
commands; #使用变量$var
done #list 可以是一个字符串,也可以是一个序列
echo {1..50} echo {a..z}
for ((i=0; i<10; i++))
{
commands; #使用变量$i
}
##while
while condition
do
commands;
done
#用true作为循环条件可以生成无限循环

##until
x=0;
until [ $x -eq 9 ];
do
let x++; echo $x;
done

文件系统相关测试

[ -f $file_var ]: 如果给定的变量包含正常的文件路径或文件名,返回真
[ -x $var ]: 如果给定的变量包含的文件可执行,返回真
[ -d $var ]:如果给定的变量是目录,则返回真
[ -e $var ]:如果给定的变量包含的文件存在,返回真
[ -c $var ]:如果给定的变量包含的是一个字符设备文件的路径,返回真
[ -b $var ]:如果给定的变量包含的是一个块设备文件的路径,返回真
[ -w $var ]:如果给定的变量包含的文件可写,返回真
[ -r $var ]:如果给定的变量包含的文件可读,返回真
[ -L $var ]:如果给定的变量包含的是一个符号链接,返回真

使用字符串比较时,最好用双中括号,因为有时候采用单个中括号会产生错误,所以最好避开它们

[[ $str1 = $str2 ]] #判断两个字符串是否相同
[[ $str1 == $str2 ]]
[[ -z $str1 ]] #str1是空返回真
[[ -n $str1 ]] #str1非空返回真

test

test命令可以用来执行条件检测。用test可以避免使用过多的括号。之前讲过的[]中的测试条件同样可以用于test命令例如:

if [ $var -eq 0 ]; then echo “True”; fi

也可以写成:

if test $var -eq 0 ; then echo “True”; fi

命令之乐

cat

cat命令不仅可以读取文件、拼接数据,还能够从标准输入中进行读取。从标准输入中读取需要使用管道操作符:OUTPUT_FROM_SOME COMMANDS | cat

类似地,我们可以用cat将来自输入文件的内容与标准输入拼接在一起,将stdin和另一个文件中的数据结合起来。方法如下:

$ echo ‘Text through stdin’ | cat - file.txt

在上面的代码中, -被作为stdin文本的文件名。

cat -s file #压缩相邻的空白行
cat -T file #将制表符显示为^
cat -n file #显示行号

录制并回放终端会话

#开始录制终端会话
$ script -t 2> timing.log -a output.session
type commands;
...
..
exit
#按播放命令序列输出
$ scriptreplay timing.log output.session

两个配置文件被当做script命令的参数。其中一个文件(timing.log)用于存储时序信息,描述每一个命令在何时运行;另一个文件(output.session)用于存储命令输出。 -t选项用于将时序数据导入stderr。 2>则用于将stderr重定向到timing.log。