【小知识】shell 脚本入门二,函数使用

725 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

前言

shell 是 Linux 系统中,位于用户与内核之间的桥梁,shell 将用户的命令解析之后传给内核进行运行。

shell 是解释式语言,与其他高级语言C++、Java、Python等功能不强大,但是也支持编程的相关的操作,如定义变量、数组、字符串等,还支持函数、结构语言if...else,while等

我们已经学习对shell脚本学习了两期内容:

本期,我们将继续对shell 进阶的语法如函数知识等学习,Let's go~

1. 函数的定义和调用

shell 函数的本质是一段可以重复使用的脚本代码

shell 函数定义有3种方法:

  • 方式一:function 是shell 函数定义的关键字
function first_func_demo()
{
	echo "函数的定义就这样了..."
}
  • 方式三:带了 function 关键字,可以省略函数名可省略括号
function second_func_demmo
{
	echo "函数也可以这么定义..."
}
  • 方式三:可省略function关键
third_func_demmo()
{
	echo "函数也可以这么定义..."
}

shell 调用函数

#!/bin/bash

first_func_demo
second_func_demo
third_func_demmo

shell 定义函数与Python一样简单,一秒就会。


如果要向函数传递参数,又如何呢?


shell 函数传参 以 函数名 参数1 参数2 ...形式来进行的

shell脚本函数里头有奇奇怪怪的名字如:

$0$1$2一直到$n$@$*$#...

emmm,别慌,总结在下面:

$0:    表示程序名
$1...n:表示第1个...第n个参数
$#:    表示输入参数的个数
$@:    获取传入的参数数组,可用于for循环遍历处理场景
$*:    把所有命令行参数当做一个整体

那我们继续上面的脚本,向函数传递两个参数,演示如下:

function func_demo
{
	echo "调用函数时传递参数以及读取参数就这么简单..."
	# 读取第一个参数值
	echo $1
	# 读取第二个参数值
	echo $2
        # 读取总数
        echo $#
}

# 1) 向函数传递两个参数,如果参数更多,空格分界,以此类推...
num1=10
num2=100
func_demo $num1 $num2 

直接复制上面的代码跑一下吧,又是一秒就会。

2. 函数的返回值

shell 会把函数当作一个小型脚本,运行结束时会返回一个状态码。

默认情况,函数的退出状态码是函数中最后一条命令返回的退出状态码。

但这样你就无法得知函数里头的代码是否都被正确的执行,因此可以借助shell脚本的函数返回值

shell 函数返回值有两种方式:

  1. return语句

shell 函数返回值,可以借助return语句来进行返回值。

return 只能返回整数值

function func1 {
	result=200
	return $result
}

func1
echo "func1 run result: $?"
  1. echo输出来返回函数值

echo 可以返回任何数据类型的数据

function func2 {
	result=200
	echo $result
}

echo "func2 run result: $(func2)"

3. 在.bashrc文件中定义函数

shell 每次启动时会重新载入.bashrc

(ps:Mac是用zsh,所以是.zshrc,为了名字通用,我们都先同意叫bashrc吧)

bashrc 文件一般放在/home/linux用户名下,我们可以定义自己工具类函数,用于处理日常一些繁琐的工作流程,使其自动化。

这里举两个栗子吧:


1.比如每天都要更新所有的git仓库并进行备份;

2.常常很多命令没记错,比如dumpsys package、meminfo、activity,am,pm等等以及其他串口命令,可以自定整理一份文档之后,之后通过调用函数直接打印这些常用命令


下面开始演示,我就定义个简单的栗子吧,在Mac中.zshrc文件中添加:

# 在.zshrc追加函数
function addem {
	echo $[ $1 + $2 ]
}

重启zsh终端,然后运行试试:

$ addem 10 20
30

4. 命令行参数的处理

上面学习了调用函数如何输入参数和读取参数。

那如果执行某个脚本文件或者执行某个命令行的参数输入、读取呢?

其实跟函数参数的输入、读取基本一致,直接上小结吧

1.命令行参数的读取,$0:程序名,$1,$2...表示第一个、第二个参数依次类推
2.特殊参数变量:$#(返回命令参数的个数)
3.获取所有命令行参数:$*(把所有命令行参数当做一个整体)、$@(获取命令行数组,主要用于for循环)

这3点问题不大,直接上个小demo演示,juejin.sh脚本内容如下:

#!/bin/bash
echo $0
echo $1
echo $2

在终端执行juejin.sh脚本结果演示:

$ sh /Users/xxx/Desktop/juejin.sh  HaHa 123
/Users/xxx/Desktop/juejin.sh
HaHa
123

5. 命令行选项的处理

命令行选项也没什么特殊的。

在命令行上,它们紧跟在脚本名之后,就跟命令行参数一样。

实际上,如果愿意,你可以像处理命令行参数一样处理命令行选项。

选项是跟在单破折线后面的单个字母,根据不同的选项执行不同的逻辑,有点像switch语句。juejin.sh演示脚本如下:

#!/bin/bash
while [ -n "$1" ]
do
	case "$1" in
		-a) echo "Found -a option" ;;
		-b) echo "Found -b option" ;;
		 *) echo "$1 option not found" ;;
	esac 
	shift
done

在终端执行juejin.sh脚本并输入选项试试:

$ sh juejin.sh  -a -b -c -d
Found -a option
Found -b option
-c option not found
-d option not found

那命令行参数和命令行选项同时用呢?


通过双破折线(--)来实现参数和选项的分离。

shell会用双破折线来表明选项列表的结束,在双破折线之后,脚本就可以放心地读剩下的命令行参数了,而不是当选项处理。

#!/bin/bash
while [ -n "$1" ]
do
	case "$1" in
		-a) echo "Found -a option" ;;
		-b) echo "Found -b option" ;;
		--) shift
		    break ;;
		 *) echo "$1 option not found" ;;
	esac 
	shift
done

count=1
for param in $@
do
	echo "Param: $param"
	let count+=1
done

我们执行上面的juejin.sh脚本,并传入选项参数、命令行参数,得到执行结果如下:

$ sh script.sh  -a -b -c -d -- HAHA 123
Found -a option
Found -b option
-c option not found
-d option not found
Param: HAHA
Param: 123

那如果有些选项参数需要传值呢?


我们在继续基于上面的脚本修改:

#!/bin/bash
while [ -n "$1" ]
do
	case "$1" in
		-a) echo "Found -a option" ;;
		-b) param_value="$2"
		    echo "Found -b option with value $param_value"
		    shift ;;
		--) shift
		    break ;;
		 *) echo "$1 option not found" ;;
	esac 
	shift
done

count=1
for param in $@
do
	echo "Param: $param"
	let count+=1
done

脚本修改之后的执行结果:

$ sh juejin.sh  -a -b ss -c -d -- HAHA 123
Found -a option
Found -b option with value ss
-c option not found
-d option not found
Param: HAHA
Param: 123

6. getopt命令

有时候我们需要合并选项参数,比如这样的格式:

$ sh juejin.sh -ab

getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将它们转换成适当的格式。

getopt的命令格式如下:

getopt optstring params


optstring:是关键所在,它定义了命令行所有需要输入的选项参数字母,如果选项参数需要带值,则在对应的字符后面加冒号(:)


有点绕?我们用上面在终端运行的脚本作为栗子:

$ sh juejin.sh  -a -b ss -c -d -- HAHA 123

一眼就看到juejin.sh脚本的选项参数有,abcd,其实b还带参数值,

那如果用getopt命令来处理后面这一大串选项、参数会是怎样的呢?

我们在终端尝试运行下面的命令:

$ getopt ab:cd -a -b ss -cd HAHA 123
-a -b ss -c -d -- HAHA 123

在命令选项的工作机制之后,假设我们以后想了解某个命令行怎么用?可以怎么做呢?还是做个demo吧:

function read_params
{
	while [[ -n $1 ]]
	do
		case $1 in
			-a) echo "Found option -a" ;;
			-b) echo "Found option -b" 
				echo "Value is $2"
				shift ;;
			--) shift
				break ;;
			 *) echo "Option not found" ;;
		esac
		shift
	done

	count=1
	for param in $@
	do
		echo $param
		let count+=1
	done
}

read_params `getopt ab: $*`

然后在终端就可以:

$ sh juejin.sh -ab HAHA 123 456

7. 总结

本期,继续对shell语法之函数进行学习,shell 语言虽然不像高级语言那样强大,但是作为程序语言,也具有高级语言基本特点。

函数本质就是把重复代码进行封装,可以进行重复使用

在shell脚本中,我们可以把实现具体的功能,使用函数封装起来,后续可以方便的进行调用使用

以上是本期内容,欢迎大佬们点赞评论,下期见~♥️