shell函数

一、函数介绍

1.1 什么是函数

函数就是用来盛放一组代码的容器,函数内的一组代码完成一个特定的功能,称之为一组代码块,调用函数便可触发函数内代码块的运行,这可以实现代码的复用,所以函数又可以称之为一个工具

1.2 为何要有函数

  1. 减少代码冗余
  2. 提升代码的组织结构性、可读性
  3. 增强扩展性

1.3 语法

#语法:
[ function ] funname [()]
{
    命令1;
    命令2;
    命令3;
    ...
    [return int;]
}

# 示例1:完整写法
function 函数名() {
	函数要实现的功能代码
}

# 示例2:省略关键字(),注意此时不能省略关键字function
function 函数名 {
	函数要实现的功能代码
}

# 示例3:省略关键字function
函数名() {
	函数要实现的功能代码
}

1.4 调用函数

# 语法:
函数名  # 无参调用
函数名 参数1 参数2  # 有参调用

# 示例
function test1(){
    echo "执行第一个函数"
}


function test2 {
    echo "执行第二个函数"
}


test3(){
    echo "执行第三个函数"
}


# 调用函数:直接引用函数名字即调用函数,会触发函数内代码的运行
test1
test2
test3

1.5 总结

具备某一功能的工具=>函数
事先准备好工具=>函数的定义
遇到应用场景,拿来就用=>函数的调用

所以函数的使用原则:先定义,后调用

二、函数参数

如果把函数当成一座工厂,函数的参数就是为工厂运送的原材料

  • 调用函数时可以向其传递参数 (类似于python中的位置实参)
# 调用函数test1,在其后以空格为分隔符依次罗列参数 
test1 aa bb cc dd 
  • 在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数…当n>=10时,需要使用${n}来获取参数
[root@manager function]# cat test1.sh 
#!/bin/bash
function test1(){

	echo "...........start........"
	echo $1
	echo $2
	echo $3
	echo "...........stop..........."
}
# 调用函数
test1 狄仁杰 程咬金 甄姬



[root@manager function]# sh test1.sh 
...........start........
狄仁杰
程咬金
甄姬
...........stop...........

在脚本内获取脚本调用者在命令行里为脚本传入的参数,同样使用的是$n,不要搞混,命令行后的参数是给脚本传递的,函数体内的参数是需要函数名后传递的位置参数


[root@manager function]# cat test2.sh 
function test2(){
    echo "...start..."
    echo "这是函数内的参数:$1"
    echo "这是函数内的参数:$2"
    echo "这是函数内的参数:$3"
    echo "...end..."
}

# 调用函数,给函数传递位置参数
test2 狄仁杰 程咬金 甄姬




# 运行,给脚本传参
[root@manager function]# sh test2.sh 苹果 香蕉 葡萄
...start...
这是函数内的参数:狄仁杰
这是函数内的参数:程咬金
这是函数内的参数:甄姬
...end...
这是脚本级的参数:苹果
这是脚本级的参数:香蕉
这是脚本级的参数:葡萄

2.1 元字符补充

参数处理说明
$*所有参数
$#传递到脚本或函数的参数个数
$@所有参数,与$*类似
$$当前脚本进程的ID号
$?获取上一条命令执行完毕后的退出状态。0表示正确,非0代表错误,
如果执行的是函数那么$?取的是函数体内return后的值
1.当$*和$@没有被引号引用起来的时候,它们确实没有什么区别,都会把位置参数当成一个个体。

2."$*" 会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$*"为空,
如果有两个位置参数并且分隔符为空格时,"$*"相当于"$1 $2"

3."$@"  会把所有位置参数当成一个单独的字段,如果没有位置参数,
则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,
则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1"  "$2"等等
[root@manager function]# cat test3.sh 
function test3(){
    echo "\$*: $*"  
    echo "\$@: $@" 
    echo $#    # 5
    echo $$    # 87380
    echo $?    # 0
}


[root@manager function]# sh test3.sh 
$*: 111 222 333 444 555 # 其实是一个参数 "111 222 333 444 555"
$@: 111 222 333 444 555 # 是多个独立的参数"111" "222" "333" "444" "555"
5
3840
0



[root@manager function]# cat test4.sh 
function test4(){
	for i in "$@"
	do
		echo $i
	done

}
# 无论传递多少参数,"$@" 想当于存放这些独立参数的列表
test4 11 22 33 "44 55" # 注意"44 55"被引号引成了一个参数


[root@manager function]# sh test4.sh 
11
22
33
44 55



[root@manager function]# cat test5.sh 
function test5(){
	for i in "$*"
	do
		echo $i
	done

}
# 无论传递多少参数,"$*" 只想当于一个参数
test5 11 22 33 "44 55" # 注意"44 55"被引号引成了一个参数

[root@manager function]# sh test5.sh 
11 22 33 44 55

2.2 nginx 启停脚本

函数式编程的核心是,尽量把函数的功能写的独立,高内聚低耦合,业务逻辑判断尽量在主程序中执行,这样函数就可以重复调用,增强扩展性

[root@manager function]# cat nginx.sh 
#!/bin/bash
. /etc/init.d/functions

function nginx_state(){
	netstat -lntp | grep nginx &>/dev/null
	if [ $? -eq 0 ];then
		return 0 # 表示nginx已经启动
	else
		return 1 # 表示nginx未启动
	fi
}


function nginx_control(){
	if [ $? -eq 0 ];then
		action "nginx $1 is ok!" /bin/true

	else
		action "nginx $1 is error!" /bin/false
	fi
}

# 启动函数
function nginx_start(){
	/usr/sbin/nginx		
}

# 停止函数
function nginx_stop(){
	/usr/sbin/nginx -s stop
}

# 重载函数
function nginx_reload(){
	/usr/sbin/nginx -s reload
}


case $1 in
start)
	nginx_state
	if [ $? -eq 0 ];then
		action "nginx 已经是启动状态,请不要重复启动" /bin/false
	else
		nginx_start
		nginx_control $1
	fi
	;;

stop)
	nginx_state
	if [ $? -ne 0 ];then
		action "nginx 已经是停止状态,请不要重复停止" /bin/false
	else
		nginx_stop	
		nginx_control $1
	fi
	;;

reload)
	nginx_state	
	if [ $? -ne 0 ];then
		echo "nginx 目前是停止状态,需要启动后才能reload,正在尝试启动"
    	nginx_start
		nginx_control start
		sleep 1
	fi
	
	/usr/sbin/nginx -s reload 
	nginx_control $1
	;;

*)
	echo "USAGE: $0 [ start | stop | relload ]"
esac

三、函数的返回值

如果把函数当成一座工厂,函数的返回值就是工厂的产品,在函数内使用return关键字返回值,函数内可以有多个return,但只要执行一个,整个函数就会立刻结束。

需要注意的是shell语言的函数中,通常用return返回函数运行是否成功的状态,0代表成功,非零代表失败,需要用$?获取函数的返回值。 return可以返回0-255的整数。

  1. 如果函数内没有return,那么将以最后一条命令运行结果(命令运行成功结果为0,否则为非0)作为返回值
[root@manager return]# cat 1.sh 
function test1(){
    echo 111
    return 
    echo 222
    return 
    echo 333
}

test1

[root@manager return]# sh 1.sh 
111

  1. 如果函数内有return,那么return后跟的只能是整型值并且范围为0-255,用于标识函数的运行结果是否正确, 与C语言和Python正好相反,shell 语言中 0 代表 true,0 以外的值代表 false

[root@manager return]# cat 2.sh 
function test1(){
    echo 111
    echo 222
    echo 333
    return 5
}

test1


[root@manager return]# sh 2.sh 
111
222
333
[root@manager return]# echo $?
5

可以看出 $? 为return的返回值,前提是值必须在(0-255)之间

  1. echo返回值:使用echo可以返回任何字符串结果,通常用于返回数据,比如一个字符串值或者列
    表值。
[root@manager return]# cat get_user.sh 
#!/bin/bash
function get_user(){
	users=$(cat /etc/passwd | awk -F ":" '{print $1}')
	echo $users
}

user_list=$(get_user)
for user in $user_list
do
	echo $user
done

3.1 实现跳板机

需求:使用case、循环、函数、实现JumpServer跳板机功能。
1.用户登陆该服务器则自动执行该脚本。
2.脚本提示可连接主机列表。
3.该脚本无法直接退出。

【trap信号列表】

下面列出一些比较重要的信号(括号里面的数字是传统的信号编号)

信号          说明 

HUP(1)      挂起,通常因终端掉线或用户退出而引发 

INT(2)      中断,通常因按下Crtl+C组合健而引发                 

QUIT(3)     退出,通常因某些严重的执行错误而引发         

ABRT(6)     中止,通常因某些严重的执行错误而引发         

ALRM(14)    报警,通常用来处理超时 |

TERM(15)    终止,通常在系统关机时发送 

TSTP(20)    停止进程的运行,但该信号可以被处理和忽略,用户健入SUSP字符时(通常是Ctrl-Z)发出这个信号
[root@manager return]# cat jumpserver.sh 
#/bin/bash
function inventory_info(){
cat << EOF
****************开源Jumpserver堡垒机*************************
1) 172.16.1.3
2) 172.16.1.4
3) 172.16.1.5
4) 172.16.1.6
5) 172.16.1.7
6) 172.16.1.8
*************************************************************
EOF
}

# 先打印菜单
inventory_info

trap "" HUP INT TSTP

while :
do
	read -p "请输入要登录的主机:" action
	case $action in
	1)
		ssh root@172.16.1.3
		;;
	 2)
		ssh root@172.16.1.4
		;;
	 3)
		ssh root@172.16.1.5
		;;
	 4)
        ssh root@172.16.1.6
        ;;
     5)
        ssh root@172.16.1.7
        ;;
     6)
        ssh root@172.16.1.8
        ;;       
	 h)
		clear
		inventory
		;;
	exec)
		exit
		;;
	*)
		continue

esac

done


将脚本添加/etc/bashrc,登陆ssh后则会执行该脚本

3.2 实现多服务一键安装

目前只实现了nginx多版本的源码安装,其他两个服务等后续完成。

1.目录结构

[root@manager nginx_install]# tree 
.
├── install_nginx # 安装nginx脚本
├── main.sh  # 主函数入口
└── menuinfo  # 菜单

2.主菜单文件

[root@manager nginx_install]# cat menuinfo 

#一级菜单
function main_menu(){
	cat << EOF
---------主菜单----------
| 1) 安装 nginx         |
| 2) 安装 mysql         |
| 3) 安装 php           |
| 4) 退出               |
--------------------------
EOF
}

function nginx_menu(){
	cat << EOF
---------nginx version----
| 1) 安装nginx 1.16      |
| 2) 安装nginx 1.18      |
| 3) 安装nginx 1.20      |
| 4) 返回上级菜单         |
-------------------------
EOF
}

function mysql_menu(){
    cat << EOF
-------mysql version-----
| 1) 安装mysql 5.6      |
| 2) 安装mysql 5.7      |
| 3) 安装mysql 8.0      |
| 4) 返回上级菜单        |  
-------------------------
EOF
}

function php_menu(){
    cat << EOF
---------php version------
| 1) 安装php   5.5      |
| 2) 安装php   5.6      |
| 3) 安装php   7.1      | 
| 4) 返回上级菜单        | 
-------------------------
EOF
}

3.主程序入口

[root@manager nginx_install]# cat main.sh 
. ./menuinfo
. ./install_nginx

main_menu
while :
do
	read -p "请输入功能编号:" num

	case $num in
		1)
		# 打印nginx安装菜单 
		nginx_menu
		while true
		do
			read -p "请输入需要安装的nginx版本号:" num_nginx

			case $num_nginx in
				1)
					var_nginx 1.16.1
					wget_nginx
					uncompress_nginx
					make_nginx
					start_nginx
			
					;;
				2)
					var_nginx 1.18.0
                    wget_nginx
                    uncompress_nginx
                    make_nginx
                    start_nginx
					;;
				3) 
					var_nginx 1.20.1
                    wget_nginx
                    uncompress_nginx
                    make_nginx
                    start_nginx
					;;
				4)
					clear
					main_menu 
					break
					;;
				5) 
					continue
				
	        esac
		done
		;;
		2)
		# 打印mysql安装菜单
		mysql_menu
		;;
		3)
		# 打印php安装菜单
		php_menu
		;;
		4)
		exit
		;;
		*)
		continue
	esac

done

4.nginx安装脚本


[root@manager nginx_install]# cat install_nginx
. /etc/init.d/functions
. ./menuinfo

var_nginx(){
nginx_version=$1
nginx_install_dir=/usr/local/nginx
nginx_download_dir=/opt
ip=$(ifconfig eth0 | grep "inet " | awk '{print $2}')
}



function wget_nginx(){
   # 安装nginx依赖包
   yum install -y gcc gcc-c++ autoconf pcre pcre-devel make automake httpd-tools  zlib-devel openssl-devel &>/dev/null
	echo "正在安装相关依赖"

	# 下载软件
	if [ -f $nginx_download_dir/nginx-$nginx_version.tar.gz ];then
		action "nginx-$nginx_version.tar.gz 已经存在,无需重复下载" /bin/false
	else
		echo "正在下载nginx-$nginx_version,请稍后..."
		wget -O $nginx_download_dir/nginx-$nginx_version.tar.gz https://nginx.org/download/nginx-${nginx_version}.tar.gz &>/dev/null
		if [ -f $nginx_download_dir/nginx-$nginx_version.tar.gz ];then
			action "nginx-$nginx_version.tar.gz 下载成功" /bin/true
		else
			action "nginx-$nginx_version.tar.gz 下载失败" /bin/false
			exit
 		fi
	fi
}

# 先检查压是否已解压,后决定是否解压
function uncompress_nginx(){
	if [ -d $nginx_download_dir/nginx-$nginx_version ];then
		action "nginx-$nginx_version 已经解压过了,无需重复解压" /bin/false
	else
		cd $nginx_download_dir && tar -xf $nginx_download_dir/nginx-$nginx_version.tar.gz &>/dev/null

		if  [ -d $nginx_download_dir/nginx-$nginx_version ];then
			action "nginx-$nginx_version 解压成功" /bin/true
		else
			action "nginx-$nginx_version 解压失败" /bin/false
			exit
		fi
	fi

}



#先检查是否被编译过,后决定是否编译
function make_nginx(){
	if [ -d $nginx_install_dir-$nginx_version ];then
		action "$nginx_download_dir/nginx-$nginx_version已经被编译过,尝试重新创建软连接"
		
		# 判断是否已存在之前的软连接
		if [ -L $nginx_install_dir ];then
			rm -rf $nginx_install_dir
		fi
		
		# 创建新的软连接	
		ln -s $nginx_install_dir-$nginx_version $nginx_install_dir
		if [ -L $nginx_install_dir ];then 
			action "nginx重新创建软连接成功" /bin/true
		else
			action "nginx重新创建软连接失败" /bin/false
				exit
		fi
	else
		# 执行编译	
		echo "正在编译和安装nginx-$nginx_version,请稍后...."
		cd $nginx_download_dir/nginx-$nginx_version && ./configure --prefix=$nginx_install_dir-$nginx_version &>/dev/null && \
			 make &>/dev/null && make install &>/dev/null
		if [ $? -eq 0 ];then
			 if [ -L $nginx_install_dir ];then
            	rm -rf $nginx_install_dir
        	 fi
            
        	ln -s $nginx_install_dir-$nginx_version $nginx_install_dir
        	if [ -L $nginx_install_dir ];then 

           		 action "nginx-$nginx_version编译成功,创建软连接成功" /bin/true
        	else
            	 action "nginx-$nginx_version编译成功,创建软连接失败" /bin/false
			 	exit
			fi
		else
			action "nginx-$nginx_version编译失败" /bin/false
			exit
		fi
	 		
	fi
} 

# 检查nginx状态
function check_nginx(){
	netstat -lntp | grep nginx &>/dev/null
	if [ $? -eq 0 ];then
		return 0
	else
		return 1
	fi
}


# 启动
function start_nginx(){
	check_nginx
	# 说明nginx已经启动了需要先杀死
	if [ $? -eq 0 ];then
		# 过滤nginx master 进程号
		id_nginx=$(ps -ef |grep nginx  | grep " 1 " | awk '{print $2}')
		# 杀死进程
		kill $id_nginx
		sleep 2
		#判断是否杀死成功
		check_nginx
		if [ $? -eq 0 ];then
			action "已有nginx进程正在运行,尝试杀死,但未杀死" /bin/false
			exit
		else
			action "已有nginx进程正在运行,尝试杀死,已杀死" /bin/true
		fi	
	fi
	# 直接启动nginx
	${nginx_install_dir}/sbin/nginx
	# 检查是否启动成功
	check_nginx
	if [ $? -eq 0 ];then
		action "nginx启动成功,可以通过 http://$ip:80 进行访问" /bin/true
	else
		action "未知错误,nginx启动失败,需要手动启动" /bin/fasle	
	fi
	# 重新调用菜单
	main_menu
	break # 结束循环,进入上级菜单,也可以返回 exit直接退出脚本
	
}


#var_nginx 1.16.1
#wget_nginx
#uncompress_nginx
#make_nginx
#start_nginx

测试1.重复安装已经装过的版本

在这里插入图片描述

测试2.安装全新版本
在这里插入图片描述

四、 变量的作用域

Shell 变量的作用域(Scope),就是 Shell 变量的有效范围(可以使用的范围)。

4.1 局部变量:只能在函数内访问

使用local关键字定义在函数内的变量属于局部范围,只能在函数内使用,如下所示

[root@manager shell]# cat hello.sh 
#!/bin/bash

# 定义函数
function test(){
    local x=111
    echo "函数内访问x:$x"
}

# 调用函数
test

echo "在函数外即全局访问x:$x"  # 无法访问函数内的局部变量

执行结果:

[root@manager  shell]# ./hello.sh 
函数内访问x:111
在函数外即全局访问x:

4.2 全局变量:可以在当前shell进程中使用

所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。每个 Shell 进程都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就都是全局变量。

[root@manager shell]# cat hello.sh 
#!/bin/bash
x=2222

function test(){
    echo "函数内访问x:$x"
}
test
echo "在函数外即全局访问x:$x" 

执行结果:

[root@manager shell]# ./hello.sh 
函数内访问x:2222
在函数外即全局访问x:2222

需要注意:

  1. 在函数内定义的变量,如果没有用local声明,那么默认也是全局变量,shell变量语法的该特性与js的变量是类似的(在js函数内部定义的变量,默认也是全局变量,除非加上关键字var)
[root@manager shell]# cat hello.sh 
#!/bin/bash
function test(){
    x=2222  # 全局变量
}
test
echo "在函数外即全局访问x:$x"  

[root@manager shell]# ./hello.sh 
在函数外即全局访问x:2222
  1. 每执行一个解释器,都会开启一个解释器的shell进程,每个shell进程都有自己的作用域彼此互不干扰
[root@manager shell]# x=111  # 该变量仅仅只在当前shell进程中有效,对新的shell进程无影响
[root@manager shell]# echo $x
111
[root@manager shell]# bash  # 执行bash解释器,则开启一个新的进程,或者干脆直接打开一个新的终端
[root@manager shell]# echo $x

[root@manager shell]#
  1. 全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个 Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。
[root@manager shell]# echo $x

[root@manager shell]# cat hello.sh 
#!/bin/bash
function test(){
    x=2222  # 全局变量
}
test

[root@manager shell]# source hello.sh  # 在当前shell进程中执行,产生一个全局变量x
[root@manager shell]# echo $x  # 在当前shell进程中访问全局变量x,可以看到
2222
[root@manager shell]# 
[root@manager shell]# 
[root@manager shell]# cat aaa.sh 
#!/bin/bash
echo $x

[root@manager shell]# source aaa.sh # 在当前shell进程中访问全局变量x,同样可以看到
2222

结论:函数test内的全局变量x早已超越了文件,即全局变量是超越文件的,作用范围是整个当前bash进程

4.3 环境变量:在当前进程的子进程中都可以使用

全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。

环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。

[root@manager shell]# export y=333  # 爷爷
[root@manager shell]# bash  # 爸爸
[root@manager shell]# echo $y
333
[root@manager shell]# bash  # 孙子
[root@manager shell]# echo $y
333

ps:通过exit命令可以一层一层地退出 Shell。

ps: set env 和export
参考

set 显示当前shell的变量(本地变量),包括当前用户的变量( 环境变量)

env 显示当前用户的变量  (环境变量)

export 显示当前导出成用户变量的shell变量 (环境变量)

注意:

  1. 环境变量只能向下传递而不能向上传递,即“传子不传父”。
  2. 两个没有父子关系的 Shell 进程是不能传递环境变量的
  3. 我们一直强调的是环境变量在 Shell 子进程中有效,并没有说它在所有的 Shell 进程中都有效;如果你通过终端创建了一个新的 Shell 窗口,那它就不是当前 Shell 的子进程,环境变量对这个新的 Shell 进程仍然是无效的。
  4. 环境变量也是临时的
[root@manager ~]# ps aux | grep [b]ash$
root       2757  0.0  0.2 116188  2844 pts/0    Ss   09:40   0:01 -bash
root      22407  0.0  0.2 115944  2452 pts/0    S    23:33   0:00 bash
root      22420  0.0  0.2 115944  2456 pts/0    S    23:33   0:00 bash

注意:
# -开头的bash代表是在登录终端登录的顶级shell进程
# 非-开头的bash代表的是子shell进程
一旦退出了在终端登录的顶级shell,那么该终端下开启的所有子shell都会被回收,export设置的环境变量随即消失

所以说环境变量也是临时的,如果想设置成永久的,需要将变量写入shell配置文件中才可以,Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。

五、登录shell与非登录shell

  1. 登录shell:就是通过输入用户名 密码后 或 su - 用户名 获得的shell
  2. 非登录shell:则是通过bash命令和脚本开启的shell环境

那么他们有什么区别呢?和我们永久设定环境变量又有什么关系呢?

我们知道在linux里一切皆为文件,同样,shell的属性加载也是写到文件里的
在登陆时就会加载对应文件的内容来初始化shell环境,
非登录与登录区别就在于加载的文件不同 从而导致获得的shell环境不同
我们看看登录shell都加载了那些文件
--> /etc/profile
--> /etc/profile.d/*.sh
--> $HOME/.bash_profile
--> $HOME/.bashrc
--> /etc/bashrc
再看非登录shell加载的文件,非登录shell加载的文件要少很多
--> $HOME/.bashrc
--> /etc/bashrc
--> /etc/profile.d/*.sh

通常,我们会将环境变量设置在 $HOME/.bash_profile

但如果不管哪种登录shell都想使用的变量 可以考虑设置在 $HOME/.bashrc或者/etc/bashrc中,因为它们都属于无论如何都会执行的文件,但是,如果我们真的在这类文件中添加了变量,那么意味着每次执行shell都会重新定义一遍该变量,而定义变量是要耗费内存资源的,这非常不可取,所以我们通常会结合export在/etc/profile文件中声明一个全局变量,这样在每次登录用户时产生的顶级shell里会有一个全局变量,所有的子shell都可以看到了,无需重复定义

[root@manager ~]# vim /etc/profile
[root@manager ~]# head -2 /etc/profile
# /etc/profile
name="bertwu"
[root@manager ~]# echo $name

[root@manager ~]# source /etc/profile  # 可以在当前shell中立即生效,也可以退出后重新登录终端
[root@manager ~]# echo $name
bertwu
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值