您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

Shell 函数

1. Shell 概述

Shell 和其他语言一样,也有,其本质就是一段可以复用的。将数据进行抽离处理,传递不同的值不同的结果,在指定的地方进行即可。

如果我们要重复执行一批相同的操作,不想重复去写,可以将这一系列操作抽象为,后期可以利用变量传值该,从而大大减少重复性劳动,提升效率减少量,使得 Shell 脚本更加的灵活和通用。

2. Shell 操作

function fnname() { 
	statements	return value}

对各个部分的说明:

function 是 Shell 中的关键字,专门用来定义;

fname 是名;

statements 是要执行的,也就是一组语句;

return value 表示的返回值,其中 return 是 Shell 关键字,专门用在中返回值,这一部分可以写也可以不写。

由大括号包围的部分称为体,,实际上就是执行体中的。

例如:

function checkuser() { 
	echo "当前为:$USER"return 0}

如上就定义了 checkuser ,其当前系统的,返回值为 0。

名后无括号

#简化写法1function fname{ 
	statements	return n}

不写 function

#简化写法2:fname() {
	statements	return n}

上述两种定义的都可以,但是还是建议在编写 Shell 的时候,也不要浪费这点时间,建议大家都用完整定义,写 function 关键字,在为定义带上 (),这样更加规范,而且便于别人阅读你的脚本。

Tips:在定义中需要后可以有多个空格,括号内也可以有多个空格,如果体写在一行,需要在语句末尾 ;

当定义好了,在需要使用的地方,其即可,注意需要在定义之后。

当没有参数的时候,非常简单,直接写即可,就是在特定地方执行体内的操作。

// 定义function fnname() { 
	statements	return value}// 
fname

语法

我们之前在变量一章节介绍了 Shell 脚本的参数,知道了参数的重要性质及其各种类特征,与 Shell 脚本传递参数一样,也可以传递参数,例如:

// 定义function fnname() { 
	statements	return value}// 
fname param1 param2 param3

如上所示,在 fname 的时候,我们传递了三个参数,参数之间利用空格分割,和 Shell 脚本传递参数一样,但需要注意 Shell 脚本中,在定义时候不能指定参数,在的时候传递参数即可,并且在时传递什么参数就接受什么参数。

上述我们了解了的定义,在其中无参即名即可,对于有参,需要传递一定的参数来执行对应的操作,的参数和脚本的参数类型及一致,在此我们简单回顾下,看参数在中都有哪些,及该如何使用。

位置参数顾名思义,就是传递给参数的位置,例如给传递参数,我们可以在执行 Shell 脚本对应位置的参数,参数的格式为:$nn 代表数字,在此需要注意与脚本传递参数不一样,$0 为依旧为脚本的,在参数传递中,例如传递给的第参数就为 $1,第 2 个参数就为 $2, 以此类推……,需要其 $0 为该的。

例如:

[root@master func]# cat f1.sh #!/bin/bashfunction () {
        echo "的第参数为: ${1}"echo "的第二个参数为: ${2}"echo "的第三个参数为: ${3}"}# 
 shell linux python go[root@master func]# bash f1.sh 的第参数为: shell
的第二个参数为: linux
的第三个参数为: python

我们可以看到传递给 f1 共 4 个位置参数,在结果中可以看到由于体内部只对三个参数进行了处理,后续的参数也就不再处理了。

在 Shell 中也存在特殊含义的参数如下表:

示例:

[root@master func]# cat f1.sh #!/bin/bashfunction fsum() {
        echo "第参数为: ${1}"echo "第二个参数为: ${2}"echo "第三个参数为: ${3}"echo "的参数总数为: ${#}"echo "的参数总数为: ${@}"local sum=for num in ${@};dolet sum=${sum}+${num}
        done
        echo "计算的总和为: ${sum}"return }# fsum    
echo $?[root@master func]# bash f1.sh 第参数为: 
第二个参数为: 
第三个参数为: 
的参数总数为: 
的参数总数为:    
计算的总和为:

如上可以看到特殊参数与 Shell 脚本传递参数一样。

Tips:局部变量需要特别声明在内部利用 local 关键字来声明。

返回值利用 $? 来接收,在上述示例中我们将计算的结果利用 echo 命令打印出来,如果我们在后续的脚本中需要利用此计算的结果,就需要得到这个返回值,此刻就需要将计算的结果不仅仅是打印而是返回了,中返回利用 return 关键字,在完成后,我们利用 $? 来接受的返回值,例如将我们上面的示例改造成返回结构的。

注意:shell 的返回值,只能是整形,并且在 0-257 之间,不能是字符串或其他形式。并且在和取得返回值之间,不能有任何操作,不然取不到 return 的值。

[root@master func]# cat f1.sh #!/bin/bashfunction fsum() {
        echo "第参数为: ${1}"echo "第二个参数为: ${2}"echo "第三个参数为: ${3}"echo "的参数总数为: ${#}"echo "的参数总数为: ${@}"local sum=for num in ${@};dolet sum=${sum}+${num}
        donereturn $sum
}


fsum    
echo $?[root@master func]# bash f1.sh 第参数为: 
第二个参数为: 
第三个参数为: 
的参数总数为: 
的参数总数为:

可以看到我们将在内部计算的数组之和,利用 return 作为返回,此刻在的时候,利用 $? 就可以拿到返回的值进一步处理。

Shell 递归,递归也就是自己自己,即在体内部又一次自己,例如:

[root@master func]# cat recursion.sh #!/bin/bashfunction myecho() {
        echo "$(date)"sleep 
        myecho inner}

myecho[root@master func]# bash recursion.sh Sat Mar  :: CST 
Sat Mar  :: CST 
Sat Mar  :: CST 
Sat Mar  :: CST 
Sat Mar  :: CST 
...

如上就是递归,在体内部又了 myecho,在执行的时候就会陷入无限循环。

3. 实例

系统经常在执行定时脚本期间会将 Linux 系统 利用率跑满,导致其他服务受到影响,故查阅资料发现有大神写的 利用率限制程序。
地址:

利用此工具可以,配合定时任务放置在服务器上,达到限制程序 情况,可根据自己系统 核心数进行参数配置,会记录 超过阀值的日志,可供后期进行查看分析。

利用编写安装 limit 工具的,如果系统不存在该命令则安装并执行 限制,如果存在则执行 limit 对超过指定 的进程进行限制,最总体 main 。

实现脚本

#!/bin/bash# Description: count file scripts# Auth: kaliarch# Email: kaliarch@163.com# function: count file# Date: 2020-03-29 14:00# Version: 1.0[ $(id -u) -gt  ] && exit # 使用超过百分之多少进行限制PEC_=# 限制进程使用百分之多少,如果程序为多线程,单个限制为85,如果为多核心,就需要按照比例写,例如为2c,像限制多线程占比80%,就写170LIMIT_=# 日志LOG_DIR=/var/log/limit/# 超过阀值进程pidPIDARG=$(ps -aux |awk -v =${PEC_} '{if($3 > ) print $2}')# 安装limit install_limit() {	[ ! - /tmp ] && mkdir /tmp ||  /tmp
	wget - https://github.com/opsengine/limit/archive/v0.2.tar.gz
	tar -zxf v0.tar.gz
	 limit && make	[ $? -eq  ] && cp src/limit /usr/bin/}# 执行limitdo_limit() {[ ! - ${LOG_DIR} ] && mkdir -p ${LOG_DIR}for i in ${PIDARG};do
	LIMITCMD=$(which limit)MSG=$(ps -aux |awk -v pid=$i '{if($2 == pid) print $0}')echo ${MSG}	[ ! - /tmp ] && mkdir /tmp ||  /tmp
	nohup ${LIMITCMD} -p $i -l ${LIMIT_} &echo "$(date) -- ${MSG}" >> ${LOG_DIR}$(date +%F).log
done
}# 主main() {	hash limit	if [ $? -eq  ];then
	  # 
		do_limit	else
		install_limit && do_limit
	fi
}

main

测试

需编写 压力测试 python 脚本,后期可以放入计划任务来监控并限制 使用率。

#!/bin/env pythonimport mathimport random

a=10000
b=10000
c=10000

sum=0for i in range(0,a):for j in range(0,b):
        randomfloat=random.uniform(1,10)randompow=random.uniform(1,10)sum+=math.pow(randomfloat, randompow)print "sum is " % sum

当我们执行 python 的 压力测试脚本后,发现单核服务 跑到了 100%。

之后运行我们的 限制脚本,可以看到 被成功限制。

在此案例中,我们着重来看 Shell ,在实例中我们编写了安装与执行 限制两个,最后编写主,在主中其他,配合定时任务就能达到限制某进程的 。

4. 注意事项

定义建议使用见名知意并需要有一定原则,不仅为了美观更是为了规范,使得其他人更好理解与阅读你的 Shell 脚本,增强脚本可维护性;

对于,必须在定义之后,Shell 运行为顺序运行,没有定义则会有异常;

定义不能指定参数,在的时候传递参数即可,并且传递什么参数,就接受什么参数;

传递参数与 Shell 脚本传递参数的类型及一致,在此可以融会贯通,对比理解记忆;

shell 的返回值,只能是整形,并且在 0-257 之间,不能为字符串或其他形式。

5. 小结

我们编写 Shell 就是为了实现简化操作,通常将数据和程序分离,我们将一组实现具体业务的逻辑编写为,也可以称为一段块,将其模块化,赋予,利用可以达到复用,使得数据与逻辑分离,脚本更加便于维护和管理。


联系我
置顶