Bash的函数和命令行参数详解

时间:2021-11-06 14:27:51

Bash不但支持函数编程,还支持递归调用;可以通过命令行向脚本传递参数

本文主要谈谈脚本编程的函数思想


函数的写法和调用

函数写法
/*可以直接写函数名加圆括号*/
func(){
...
cmd
...
[return value] /*返回值是可选的*/
}
/*也可以在函数名前加上function关键字*/
function func(){
...
cmd
...
[return value] /*返回值是可选的*/

函数调用

func /*直接用函数名调用(不加括号),切必须写在函数定义的后面*/

命令行参数

普通参数

可以通过命令行把参数传递个脚本程序或者其子程序(函数)

#!/bin/bash
#Filename: script.sh
func(){
echo "$1"
}
func $1
---------------------
~> ./script.sh hello world
~> hello

特殊参数
$0:文件名
$1 $2 $3:传入的第1、2、3个命令行参数(以此类推)
$#:传入的命令行参数长度
$*:用于指代传入的每一个参数
$@:类似上一条指令,该标识把传入的所有参数视为一个整体
$?:给出上一条命令的状态返回值(正常退出时为0)


递归函数

和一般的编程语言一样,脚本编程也支持递归

F(){echo $1 F hello}  /*该函数将不断打印“hello”*/
/*Fork炸弹*/
/*递归函数,不断生成新的进程,最终造成拒绝服务攻击*/
/*可以通过修改配置文件/etc/security/limits.conf来限制可生成的最大进程数来避开Fork炸弹攻击*/
---------------------------------------------------

:(){:|:& };:
---------------------------------------------------

其中,
:()定义新函数,函数名为:
:|:递归调用+管道命令,将前一个函数的输出引到另一次调用该函数;总的作用是每次调用函数:时会分成两份拷贝
&调用间脱钩,即函数调用前的&将子进程放入后台时,使得函数被杀死后为其所调用的两个:函数还能继续执行
;函数定义结束后立即进行下一步操作
:引爆fork炸弹
---------------------------------------------------

fork炸弹只是一种递归调用的思想,在具体语言中形式变化多样
---------------------------------------------------

/*Perl版本的fork炸弹*/
fork while fork

导出函数

函数也能像环境变量一样用export命令导出;这样一来函数的作用域就可以扩展到子进程中

export -f fname

使用read命令读入参数

read var                /*直到按下回车键从缓冲区读取数据到var*/
read -n 2 var /*读取两个字符并保存在var中*/
read -s var /*无回显读取密码*/
read -p "Enter..." var /*显示提示信息*/
read -t 5 var /*在5秒内从终端读取数据*/
read -d ":" var /*以:界定符作为输入行的结束*/

字段分隔符和迭代器

内部字段分隔符(IFS)是shell脚本编程的一个重要概念,用来把命令行分隔成字段;IFS是存储界定符的一个环境变量
IFS可以是空白符、制表符、回车符中的一个或几个;默认情况下IFS为空白符

/*IFS实战:迭代输出*/
data="name,sex,rollno,location"
IFS=, #将IFS从空白符换成逗号
for item in $data;
do
echo Item: $item
done

/*
* Item: name
* Item: sex
* Item: rollno
* Item: location
*/

循环和比较测试

循环

/*for循环,list可以是一个字符串或其它序列*/
for var in list;
do
cmd; #使用变量$var
done

# list={1..50}, list="hi,world"

------------------------------------------------

/*for循环也可以使用C语言的风格*/
for((i=0;i<10;i++))
{
cmd;
}
/*while循环,使用true作为条件将产生无限循环*/
while condition
do
cmd;
done
/*until循环*/
# e.g.
x=0;
until [$x -eq 9];
do
let x++; echo $x;
done

比较测试

/*if条件*/
if condition;
then
cmds;
fi
/*else if*/
if condition1;
then;
cmds;
else if condition2; then
cmds;
else
cmds;
fi

if-else语句可以进行嵌套,if的判断语句可能会变得很长,这时候可以用逻辑运算符使其变得尽量简洁

[condition] && action   若条件为真则执行
[condition] || action 若条件为假则执行

算数比较运算符

-gt:大于
-lt:小于
-ge:大于等于
-le:小于等于

逻辑操作符

-a:逻辑与
-o:逻辑或
!:逻辑非

切记切记!在condition和两边的’[’ ‘]’之间一定要有一个空格(否则报错!)

文件系统的相关测试和字符串的相关测试和数字略有不同:

/*文件系统的*/
-f:若给定的变量包含正常的文件路径或文件名,则返回真
-x:若给定的变量包含的文件可执行,则返回真
-d:若给定的变量包含的是目录,则返回真
-e:若给定的变量包含的文件存在,则返回真
-c:若给定的变量包含的是一个字符设备文件的路径,则返回真
-b:若给定的变量包含的是一个块设备文件的路径,则返回真
-w:若给定的变量包含的文件可写,则返回真
-r:若给定的变量包含的文件可读,则返回真
-L:若给定的变量包含的文件包含的是一个符号链接,则返回真
/*字符串比较*/
= 判断两个字符串是否内容相同
== 这是检查字符串是否相等的另一种写法(和写法一等价)
!= 判断两个字符串是否不等
-z 非字符串
-n:非空字符串 //说明,-z和-n都写在字符串变量之前如 [[ -n $str ]]

注意!在判断字符串相等的等号“=”和变量之间有一个空格!千万不要漏写,否则就不是判断相等,而成了赋值的意思!

另外,判断测试字符串时最好用两个方括号!因为有时候用单个方括号可能会引起错误!