LV02-02-shell-04-函数和文件包含

本文主要是shell——函数和文件包含相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
参考方向 参考原文
------
点击查看相关文件下载
--- ---

一、 shell 函数

1.函数的定义

1
2
3
4
5
[function] function_name() 
{
list of commands.
[ return int_value ]
}

【注意】

(1) function 是可选的,可以省略不写,直接就是函数名字也可以。

(2) return int_value 表示返回值,如果不加,将以最后一条命令运行结果,作为返回值。函数的返回值可以在调用该函数后通过 $? 来获得。

(3) Shell 中,通过 return 只能返回整数值,并且是 0-255 的范围,如果超出这个范围就会错误的结果。

(4)如果希望直接从终端调用函数,可以将函数定义在主目录下的 .profile 文件,这样每次登录后,在命令提示符后面输入函数名字就可以立即调用。

2.函数的调用

1
2
3
4
# 方式1
function_name [arg1 arg2 ...]
# 方式2
value_name= function_name [arg1 arg2 ... ]

【注意】

(1)函数必须先定义后使用,这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。不像 C 语言一样可以只在前边声明函数,到最后再定义。

(2) Shell 函数的调用只需要写出函数名即可,不需要写出括号 () 。

3.函数的删除

像删除变量一样,函数也可以被删除。

1
$unset .f function_name

4.函数的参数传递

4.1传参格式

在 Shell 中,调用函数时可以向其传递参数。在函数体内部,通过 $n (也就是位置变量)的形式来获取参数的值。除了 $n 还有几个特殊字符也可以用于处理参数。

${n} 位置变量
${*} 以一个单字符串显示所有向脚本传递的参数
${$} 脚本运行的当前进程ID号
${!} 后台运行的最后一个进程的ID号
${@} 与$*相同,但是使用时加引号,并在引号中返回每个参数
${?} 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
$# 传递到脚本或函数的参数个数

【注意】 最后一个 # 特殊字符在使用时也可以加 {} ,只不过这里不写是因为加上后,会导致 Hexo 渲染后的页面发生错乱。

1
2
3
4
5
6
7
8
9
# 定义函数时使用传入的参数
[function] function_name()
{
echo "$1" # 输出第1个位置变量
echo "${10}" # 输出第10个位置变量
}

# 调用函数时传入参数
function_name [ arg1 arg2 arg3 ... ]

4.2使用实例

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
function test()
{
for var in 1 2 3 4 5 6 7 8 9 10
do
echo '$'${var}' = '${var}' '
done
}

test 10 9 8 7 6 5 4 3 2 1 0

输出结果为:

1
2
3
4
5
6
7
8
9
10
$1 = 1 
$2 = 2
$3 = 3
$4 = 4
$5 = 5
$6 = 6
$7 = 7
$8 = 8
$9 = 9
$10 = 10

5.函数内部的变量

5.1两种作用域

shell 函数内部可以定义变量,有两种定义方式。两种方式代表了变量有不同的作用域。

  • 全局作用域:在脚本的其他任何地方都能够访问该变量。

  • 局部作用域:只能在声明变量的作用域内(函数内部)访问。

1
2
3
4
# 局部变量
local variable_name=value
# 全局变量
variable_name=value

5.2使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
function test()
{
var1=10
local var2=20
echo "函数内打印在函数内部定义的变量"
echo "var1=${var1}"
echo "var2=${var2}"
}

test
echo "函数外打印在函数内部定义的变量"
echo "var1=${var1}"
echo "var2=${var2}"

输出结果为:

1
2
3
4
5
6
函数内打印在函数内部定义的变量
var1=10
var2=20
函数外打印在函数内部定义的变量
var1=10
var2=

可以看出,局部变量 var2 的值在函数外是访问不到的。

二、文件包含

1.使用格式

shell 脚本与 C 语言一样,可以包含外部文件, shell 可以将外部脚本的内容合并到当前脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件以便于使用。shell 文件包含的语法格式有两种:

1
2
3
4
# 语法格式1
. file_name
# 语法格式2
source file_name

【注意】被包含的脚本文件不需要可执行权限,使用source命令和点号 . 是等价的,类似于C/C++中的#include预处理指令,都是将指定的脚本内容拷贝至当前的脚本中,由一个shell进程来执行。

2.使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#------------以下分隔开的是两个文件---------
# 引用其他文件内容的文件
#!/bin/bash
# filename: file1.sh
var1=aaa
var2=bbb
export var1
echo "file2========start"
. file2.sh
echo "file2========end"
echo ""
echo "file1.sh:var1=${var1}(file1.sh中定义,export)"
echo "file1.sh:var2=${var2}(file1.sh中定义)"
echo "file1.sh:var=${var}(file2.sh中定义)"

# --------------------------------------
# 变量定义文件
#!/bin/bash
# filename: file2.sh
var="file1.sh"

echo "file2.sh:var1=${var1}(file1.sh中定义,export)"
echo "file2.sh:var2=${var2}(file1.sh中定义)"
echo "file2.sh:var=${var}(file2.sh中定义)"

我们输入 ./file1.sh 执行脚本,输出结果如下

1
2
3
4
5
6
7
8
9
10
hk@ubuntu-22-04:~/桌面/1sharedfiles/6temp/myubuntu$ ./file1.sh 
file2========start
file2.sh:var1=aaa(file1.sh中定义,export)
file2.sh:var2=bbb(file1.sh中定义)
file2.sh:var=file2.sh(file2.sh中定义)
file2========end

file1.sh:var1=aaa(file1.sh中定义,export)
file1.sh:var2=bbb(file1.sh中定义)
file1.sh:var=file2.sh(file2.sh中定义)

三、子shell?

上边的文件包含会把另一个脚本的内容复制到此脚本中执行,就很类似于C语言中的 include,还有其他方法可以执行其他脚本吗?还有以下两种方式:

  • fork: 如果脚本有执行权限的话,path/to/foo.sh 。如果没有的话就这样:sh path/to/foo.sh。
  • exec: exec path/to/foo.sh

这两种有什么特点?与上边的source有什么不同?我们来看一看吧

1. fork

1.1 使用格式

1
2
path/to/foo.sh     # 有执行权限
sh path/to/foo.sh # 五执行权限

执行方式:在主脚本中写所要调用脚本的路径,比如 /home/hk/1sharedfiles/6temp/file1.sh

特点:

(1)运行主脚本运行的的终端会新开一个子shell来执行脚本first.sh;

(2)子shell执行的时候,父shell还在;

(3)子shell执行完毕后返回父shell,子shell会继承父shell的环境变量,但是子shell中的环境变量不会带回父shell

1.2 使用实例

  • 主调shell脚本:file1.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# filename: file1.sh

A=999
echo "before fork: PID for file1.sh = $$"
export A
echo "In file1.sh: variable file1_variable=$A"

echo -e "==>>> using fork start\n"
/home/hk/1sharedfiles/6temp/file2.sh

echo "after exec/source/fork: PID for file1.sh = $$"
echo -e "In file1.sh: variable file1_variable=$A\n"
  • 被调shell脚本:file2.sh
1
2
3
4
5
6
7
8
#!/bin/bash
# filename: file2.sh

echo "PID for file2.sh = $$"
echo "In for file2.sh get variable file1_variable=$A from file1.sh"
A=111
export A
echo -e "In for file2_variable.sh: variable for file2_variable=$A\n"
  • 执行结果如下
1
2
3
4
5
6
7
8
9
10
11
hk@ubuntu-22-04:~/1sharedfiles/6temp$ ./file1.sh 
before fork: PID for file1.sh = 6773
In file1.sh: variable file1_variable=999
==>>> using fork start

PID for file2.sh = 6774
In for file2.sh get variable file1_variable=999 from file1.sh
In for file2_variable.sh: variable for file2_variable=111

after exec/source/fork: PID for file1.sh = 6773
In file1.sh: variable file1_variable=999

2. exec

2.1 使用格式

1
exec script_path

执行方式:exec + 脚本路径

特点:

(1)运行主脚本时不会新开一个子shell来执行被调用脚本first.sh,被调用的脚本与主脚本在同一个Shell内执行,所以被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用。

(2)使用exec调用一个新脚本以后, 父脚本中exec行之后的内容就不会再执行。

2.2 使用实例

  • 主调shell脚本:file1.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# filename: file1.sh

A=999
echo "before fork: PID for file1.sh = $$"
export A
echo "In file1.sh: variable file1_variable=$A"

echo -e "==>>> using fork start\n"
exec /home/hk/1sharedfiles/6temp/file2.sh

echo "after exec/source/fork: PID for file1.sh = $$"
echo -e "In file1.sh: variable file1_variable=$A\n"
  • 被调shell脚本:file2.sh
1
2
3
4
5
6
7
8
#!/bin/bash
# filename: file2.sh

echo "PID for file2.sh = $$"
echo "In for file2.sh get variable file1_variable=$A from file1.sh"
A=111
export A
echo -e "In for file2_variable.sh: variable for file2_variable=$A\n"
  • 执行结果如下
1
2
3
4
5
6
7
8
hk@ubuntu-22-04:~/1sharedfiles/6temp$ ./file1.sh 
before fork: PID for file1.sh = 6808
In file1.sh: variable file1_variable=999
==>>> using fork start

PID for file2.sh = 6808
In for file2.sh get variable file1_variable=999 from file1.sh
In for file2_variable.sh: variable for file2_variable=111

3. source

fork 的区别是不新开一个子 Shell 来执行被调用的脚本,而是在同一个 Shell 中执行. 所以被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用。

其实从命名上可以感知到其中的细微区别,下面通过两个脚本来体会三种调用方式的不同:

3.1 使用格式

1
2
source script_path
. script_path

执行方式:source(或者 . ) + 脚本路径

特点:

(1)同exec的特点(1)即运行主脚本时不会新开一个子shell来执行被调用脚本file2.sh,被调用的脚本与主脚本在同一个shell内执行。

(2)使用source调用一个新脚本以后, 主脚本中exec行之后的内容还会执行。

(3)被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用。

3.2 使用实例

  • 主调shell脚本:file1.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# filename: file1.sh

A=999
echo "before fork: PID for file1.sh = $$"
export A
echo "In file1.sh: variable file1_variable=$A"

echo -e "==>>> using fork start\n"
. /home/hk/1sharedfiles/6temp/file2.sh

echo "after exec/source/fork: PID for file1.sh = $$"
echo -e "In file1.sh: variable file1_variable=$A\n"
  • 被调shell脚本:file2.sh
1
2
3
4
5
6
7
8
#!/bin/bash
# filename: file2.sh

echo "PID for file2.sh = $$"
echo "In for file2.sh get variable file1_variable=$A from file1.sh"
A=111
export A
echo -e "In for file2_variable.sh: variable for file2_variable=$A\n"
  • 执行结果如下
1
2
3
4
5
6
7
8
9
10
11
hk@ubuntu-22-04:~/1sharedfiles/6temp$ ./file1.sh 
before fork: PID for file1.sh = 6840
In file1.sh: variable file1_variable=999
==>>> using fork start

PID for file2.sh = 6840
In for file2.sh get variable file1_variable=999 from file1.sh
In for file2_variable.sh: variable for file2_variable=111

after exec/source/fork: PID for file1.sh = 6840
In file1.sh: variable file1_variable=111