(转)Linux Shell脚本攻略1:小试牛刀(3)
2018-10-22 本文已影响5人
千幻流光
本文为转载
作者:数据分析之路
来源:CSDN
原文:https://blog.csdn.net/qixizhuang/article/details/77918786
函数和参数
- 定义函数
function fname() { statements }
fname() { statements }
- 调用函数:
fname
; - 参数可以传递给函数,并由脚本进行访问:
fname arg1 arg2
- 参数也可以传递给脚本并通过
script:$0
(脚本名)访问。-
$1
是第一个参数。 -
$n
是第n个参数。 -
"$@"
被扩展成"$1"
、"$2"
、"$3"
等 -
"$*"
被扩展成"$1c$2c$3"
,其中c
是IFS
的第一个字符。 -
"$@"
要比"$*"
用得多。由于"$*"
将所有的参数当做单个字符串,因此它很少被使用。
-
- 在Bash中,函数同样支持递归:
F() { echo $1; F hello; sleep 1; }
- Fork炸弹:
:(){ :|:& };:
fork炸弹(fork bomb)在计算机领域中是一种利用系统调用fork(或其他等效的方式)进行的拒绝服务攻击。
与病毒与蠕虫不同的是,fork炸弹没有传染性。
fork炸弹会使对同时执行进程、程序数设限的系统无法执行新程序,对于不设限的系统则使之停止响应。
fork炸弹通过进程递归式派生(fork,亦即自我复制),以使系统拒绝服务甚至崩溃。
由Jaromil所作的在类UNIX系统的shell环境下触发fork炸弹的shell脚本代码,总共只用了13个字符(包括空格)
:(){ :|:& };:
注解如下:
:()
# 定义函数,函数名为":",即每当输入":"时就会自动调用{}内代码{
# ":"函数的开始标识:
# 用递归方式调用":"函数本身|
# 并用管道(pipe)将其输出引至...:
# 另一次递归调用的":"函数
综合3-5,:|:
表示的即是每次调用函数":"的时候就会生成两份拷贝&
# 调用间脱钩,以使最初的":"函数被杀死后为其所调用的两个":"函数还能继续执行}
# ":"函数的结束标识;
# ":"函数定义结束后,将要进行的操作...:
# 调用":"函数,"引爆"fork炸弹
其中函数名“:”只是简化的一例,实际实现时可以随意设定,一个较易理解(将函数名替换为“forkbomb”)的版本如下:
forkbomb(){ forkbomb|forkbomb &} ; forkbomb
- 导出函数
函数也能像环境变量一样用export导出,如此一来,函数的作用域就可以扩展到子进程中,如:$ export -f fname
- 读取命令返回值,如果命令成功退出则返回状态为0,否则为非0:
$ cmd; echo $?;
- 向命令传递参数。假设
-p
、-v
是可用选项,-k N
是另一个可以接受数字的选项,同时该命令还接受一个文件名作为参数,那么,它有如下几种执行方式:$ command -p -v -k 1 file
$ command -pv -k 1 file
$ command -vpk 1 file
$ command file -pvk 1
将命令序列的输出读入变量
- 输入通常是通过stdin或参数传递给命令。输出要么出现在stderr,要么出现在stdout。当我们组合多个命令时,通常将stdin用于输入,stdout用于输出。此时,这些命令被称为过滤器(filter),我们使用管道(pipe)连接每个过滤器,管道操作符是
|
。如:$ cmd1 | cmd2 | cmd3
- 我们通常使用管道并利用子shell的方式将多个文件的输出组合起来,如:
$ ls | cat -n > out.txt
- 我们可以用下面的方法读取由管道相连的命令序列的输出,这种方法被称为子shell。
$ cmd_output=$(COMMANDS)
$ cmd_output=$(ls | cat -n); echo $cmd_output;
- 另一种被称为反引用backtick(也有人称反标记)的方法也可以用于存储命令输出:
$ cmd_output=`COMMANDS` `$ cmd_output=`ls | cat -n`; echo $cmd_output;`
- 利用子shell生成一个独立的进程,可以使用()操作符来定义一个子shell:
$ pwd; (cd /bin; ls); pwd;
- 当命令在子shell中执行时,不会对当前shell有任何影响;所有的改变仅限于子shell内。
- 通过引用子shell的方式保留空格和换行符。假如我们使用子shell或反引用的方法将命令的输出读入一个变量中,可以将它放入双引号中,以保留空格和换行符
\n
。
如下图,有无双引号的类似命令,输出了不同结果:
图1
不使用回车键来读取n个字符
- read是一个重要的Bash命令,它用于从键盘或标准输入中读取文本。我们可以使用read以交互的形式读取来自用户的输入。
- 大多数编程语言的输入库是从键盘读取输入,但只有按下回车键的时候,才标志着输入完毕。而read命令提供了一种不需要按回车键就可以读取输入的方法。
- 从输入中读取n个字符并存入变量variable_name:
$ read -n number_of_chars variable_name
- 用无回显的方式读取密码:
$ read -s var
- 显示提示信息
$ read -p "Enter input:" var
- 在特定时限内读取输入(s)
$ read -t timeout var
- 用特定的定界符作为输入行的结束
$ read -d delim_char var
运行命令直至成功
有时候命令只有满足某些条件或是某种外部事件(例如文件可以被下载)操作才能够成功执行,这种情况下,我们可能希望重复执行命令,直到成功为止。
repeat()
{
while true
do
$@ && return
done
}
大多数现代系统中,true是作为/bin中的一个二进制文件来实现的,因此每执行一次while循环,shell就会生成一个进程。不想这样的话,可以使用shell内建的:
命令
repeat()
{
while : # : 命令总是会返回为0的退出码,即成功执行命令的退出码。
do
$@ && return
done
}
repeat()
{
while true
do
$@ && return
sleep 30 #访问网站的时候要控制频率,以防被加入黑名单。
done
}
字段分隔符
- 内部字段分隔符(Internal Field Separator,IFS)是shell脚本编程中的一个重要概念。在处理文本数据时非常重要。
- 内部字段分隔符适用于特殊用途的定界符(delimiter),IFS是存储定界符的环境变量,它是当前shell环境使用的默认定界字符串。
- 考虑CSV数据的情况:
data="name,sex,rollno,location" oldIFS=$IFS for item in $data; do echo Item: $item done IFS=$oldIFS
#!/bin/bash #用途:演示IFS的用法 line="root:x:0:0:root:/root:/bin/bash" oldIFS=$IFS; IFS=":" count=0 for item in $line; do [ $count -eq 0 ] && user=$item; [ $count -eq 0 ] && shell=$item; let count++ done; IFS=$oldIFS echo $user\'s shell is $shell;
迭代器
对一系列值进行迭代时,循环非常有用。Bash提供了多种类型的循环。
1. for循环
for循环的一般用法
for var in list; #list可以是一个字符串,也可以是一个序列。
do
commands;
done
-
echo {1..50}
能够生成一个从1~50的数字列表。 -
echo {a..z}; echo {a..h};
能够生成字母列表。 for i in {a..z}; do actions; done;
for循环也可以采用C语言中for循环的格式,如:
for((i=0;i<10;i++))
{
commands;
}
2. while循环
while condition
do
commands;
done
3. until循环
x=0;
until [ $x -eq 9 ];
do
let x++; echo $x;
done
比较与测试
if条件判断的几种实现方式:
- if...then...else...fi结构
if condition;
then
commands;
else if condition; then
commands;
else
commands;
fi
- 简略实现
[ condition ] && action;
#如果condition为真,则执行action;
[ condition ] || action;
#如果condition为假,则执行action。
# &&是逻辑与运算符,||是逻辑或运算符。
算数比较
-
条件通常被放置在封闭的中括号内,一定要注意在
[
或]
与操作数之间有一个空格,如果忘记了这个空格,脚本就会报错,例如:
[$var -eq 0 ]
(左[
缺少空格) or[ $var -eq 0]
(右]
缺少空格) -
对变量或值进行算术条件判断:
-
[ $var -eq 0 ]
#当$var等于0时,返回真 -
[ $var -ne 0 ]
#当$var为非0时,返回真 -
[ $var -gt 0 ]
#当$var大于0时,返回真 -
[ $var -lt 0 ]
#当$var小于0时,返回真 -
[ $var -ge 0 ]
#当$var大于等于0时,返回真 -
[ $var -le 0 ]
#当$var小于等于0时,返回真
-
-
可以结合多个条件进行测试
-
[ $var1 -ne 0 -a $var2 -gt 2 ]
#使用逻辑与-a
-
[ $var1 -ne 0 -o $var2 -gt 2 ]
#使用逻辑或-o
-
-
文件系统相关测试
-
[ -f $file_var ]
# 如果给定的变量包含正常的文件路径或文件名,则返回真。 -
[ -x $var ]
# 如果给定的变量名包含的文件可执行,则返回真。 -
[ -d $var ]
# 如果给定的变量包含的是目录,则返回真。 -
[ -e $var ]
# 如果给定的变量包含的文件存在,则返回真。 -
[ -c $var ]
# 如果给定的变量包含的是一个字符设备文件的路径,则返回真。 -
[ -b $var ]
# 如果给定的变量包含的是一个块设备文件的路径,则返回真。 -
[ -w $var ]
# 如果给定的变量包含的文件可写,则返回真。 -
[ -r $var ]
# 如果给定的变量包含的文件可读,则返回真。 -
[ -L $var ]
# 如果给定的变量包含的是一个符号链接,则返回真。
fpath="/etc/passwd" if [ -e $fpath ]; then echo File exists; else echo Does not exist; fi
-
-
字符串比较
- 使用字符串比较时,最好用双中括号,因为有时候采用单个中括号会产生错误。
-
[[ $str1 = $str2 ]]
:当str1等于str2时,返回真,也就是说,str1和str2包含的文本是一模一样的。 -
[[ $str1 == $str2 ]]
:这是检车字符串是否相等的另一种写法。 -
[[ $str1 != $str2 ]]
:如果str1和str2不相同,则返回真。 -
[[ $str1 > $str2 ]]
:如果str1的字母序比str2大,则返回真。 -
[[ $str1 < $str2 ]]
:如果str1的字母序比str2小,则返回真。 -
[[ -z $str1 ]]
:如果str1包含的是空字符串,则返回真。 -
[[ -n $str1 ]]
:如果str1包含的是非空字符串,则返回真。
if [[ -n $str1 ]] &&[[ -z $str2 ]];
then
commands;
fi
str1="Not empty "
str2=""
if [[ -n $str1 ]] && [[ -z $str2 ]];
then
echo str2 is nonempty and str2 is empty string.
fi
test命令可以用来执行条件检测
if [ $var -eq 0 ]; then echo "True"; fi
# 也可以写成:
if test $var -eq 0; then echo "True"; fi