Shell

来自小能手俱乐部
跳到导航 跳到搜索

简介

什么是 shell

  • Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。
  • Shell 既是一种命令语言,又是一种程序设计语言。
  • Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问 Linux 内核的服务。

什么是 shell 脚本

Shell 脚本(shell script),是一种为 shell 编写的脚本程序,一般文件后缀为 .sh

shell 环境

shell 编程跟 java、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。

shell 的解释器种类众多,常见的有:

sh - 即 Bourne Shell。sh 是 Unix 标准默认的 shell。
bash - 即 Bourne Again Shell。bash 是 Linux 标准默认的 shell。
fish - 智能和用户友好的命令行 shell。
xiki - 使 shell 控制台更友好,更强大。
zsh - 功能强大的 shell 与脚本语言。
指定脚本解释器

在 shell 脚本,#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。#! 被称作shebang(也称为 Hashbang )。

所以,你应该会在 shell 中,见到诸如以下的注释:

  • 指定 sh 解释器
#!/bin/sh
  • 指定 bash 解释器
#!/bin/bash

注意

上面的指定解释器的方式是比较常见的,但有时候,你可能也会看到下面的方式: #!/usr/bin/env bash

这样做的好处是,系统会自动在 PATH 环境变量中查找你指定的程序(本例中的bash)。相比第一种写法,你应该尽量用这种写法,因为程序的路径是不确定的。这样写还有一个好处,操作系统的PATH变量有可能被配置为指向程序的另一个版本。比如,安装完新版本的bash,我们可能将其路径添加到PATH中,来“隐藏”老版本。如果直接用#!/bin/bash,那么系统会选择老版本的bash来执行脚本,如果用#!/usr/bin/env bash,则会使用新版本。

模式

shell 有交互和非交互两种模式。

交互模式

简单来说,你可以将 shell 的交互模式理解为执行命令行。

看到形如下面的东西,说明 shell 处于交互模式下:

user@host:~$

接着,便可以输入一系列 Linux 命令,比如 lsgrepcdmkdirrm 等等。

非交互模式

简单来说,你可以将 shell 的非交互模式理解为执行 shell 脚本。

在非交互模式下,shell 从文件或者管道中读取命令并执行。当 shell 解释器执行完文件中的最后一个命令,shell 进程终止,并回到父进程。

可以使用下面的命令让 shell 以非交互模式运行:

sh /path/to/script.sh
bash /path/to/script.sh
source /path/to/script.sh
./path/to/script.sh

上面的例子中,script.sh是一个包含 shell 解释器可以识别并执行的命令的普通文本文件,shbash是 shell 解释器程序。你可以使用任何喜欢的编辑器创建script.sh(vim,nano,Sublime Text, Atom 等等)。

其中,source /path/to/script.sh./path/to/script.sh 是等价的。

除此之外,你还可以通过chmod命令给文件添加可执行的权限,来直接执行脚本文件:

chmod +x /path/to/script.sh #使脚本具有执行权限
/path/to/test.sh

这种方式要求脚本文件的第一行必须指明运行该脚本的程序,比如:

#!/usr/bin/env bash
echo "Hello, world!"

使用echo 命令将字符串输出到屏幕上。

基本语法

解释器

#! 决定了脚本可以像一个独立的可执行文件一样执行,而不用在终端之前输入sh, bash, python, php等。

# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash

注释

注释可以说明你的代码是什么作用,以及为什么这样写。

shell 语法中,注释是特殊的语句,会被 shell 解释器忽略。

  • 单行注释 - 以 # 开头,到行尾结束。
  • 多行注释 - 以 :<<EOF 开头,到 EOF 结束。
#--------------------------------------------
# shell 注释示例
# author:xiaoming
#--------------------------------------------

# echo '这是单行注释'

########## 这是分割线 ##########

:<<EOF
echo '这是多行注释'
echo '这是多行注释'
echo '这是多行注释'
EOF

echo

echo 用于字符串的输出。

  • 输出普通字符串:
    echo "hello, world"
    # Output: hello, world
    
  • 输出含变量的字符串:
    name=xiaoming
    echo "hello, \"${name}\""
    # Output: hello, "xiaoming"
    
  • 输出含换行符的字符串:
    # 输出含换行符的字符串
    echo "YES\nNO"
    #  Output: YES\nNO
    
    echo -e "YES\nNO" # -e 开启转义
    #  Output:
    #  YES
    #  NO
    
  • 输出含不换行符的字符串:
    echo "YES"
    echo "NO"
    #  Output:
    #  YES
    #  NO
    
    echo -e "YES\c" # -e 开启转义 \c 不换行
    echo "NO"
    #  Output:
    #  YESNO
    
  • 输出重定向至文件:
    echo "test" > test.txt
    
  • 输出执行结果:
    echo `pwd`
    #  Output:(当前目录路径)
    

printf

printf 用于格式化输出字符串。

printf 命令的语法:

printf  format-string  [arguments...]

参数说明:

  • format-string: 为格式控制字符串
  • arguments: 为参数列表。

默认,printf 不会像 echo 一样自动添加换行符,如果需要换行可以手动添加 \n

变量

跟许多程序设计语言一样,你可以在 bash 中创建变量。

bash 中没有数据类型,bash 中的变量可以保存一个数字、一个字符、一个字符串等等。同时无需提前声明变量,给变量赋值会直接创建变量。

变量命名原则

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线(_)。
  • 不能使用标点符号。
  • 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。

声明变量

访问变量的语法形式为:${var}$var

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。

word="hello"
echo ${word}
# Output: hello

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

rword="hello"
echo ${rword}
readonly rword
# rword="bye"  # 如果放开注释,执行时会报错

删除变量

使用 unset 命令可以删除变量。变量被删除后不能再次使用。unset 命令不能删除只读变量。

dword="hello"  # 声明变量
echo ${dword}  # 输出变量值
# Output: hello

unset dword    # 删除变量
echo ${dword}
# Output: (空)

变量类型

  • 局部变量 :局部变量是仅在某个脚本内部有效的变量。它们不能被其他的程序和脚本访问。
  • 环境变量 :环境变量是对当前 shell 会话内所有的程序或脚本都可见的变量。创建它们跟创建局部变量类似,但使用的是 export 关键字,shell 脚本也可以定义环境变量。

常见的环境变量:

变量 描述
$HOME 当前用户的用户目录
$PATH 用分号分隔的目录列表,shell 会到这些目录中查找命令
$PWD 当前工作目录
$RANDOM 0 到 32767 之间的整数
$UID 数值类型,当前用户的用户 ID
$PS1 主要系统输入提示符
$PS2 次要系统输入提示符

字符串

单引号和双引号

shell 字符串可以用单引号 '',也可以用双引号 “”,也可以不用引号。

  • 单引号的特点
    • 单引号里不识别变量
    • 单引号里不能出现单独的单引号(使用转义符也不行),但可成对出现,作为字符串拼接使用。
  • 双引号的特点
    • 双引号里识别变量
    • 双引号里可以出现转义字符

综上,推荐使用双引号。

拼接字符串

# 使用单引号拼接
name1='white'
str1='hello, '${name1}''
str2='hello, ${name1}'
echo ${str1}_${str2}
# Output:
# hello, white_hello, ${name1}

# 使用双引号拼接
name2="black"
str3="hello, "${name2}""
str4="hello, ${name2}"
echo ${str3}_${str4}
# Output:
# hello, black_hello, black

获取字符串长度

text="12345"
echo ${#text}
# Output:
# 5

截取子字符串

text="12345"
echo ${text:2:2}
# Output:
# 34

查找子字符串

text="hello"
echo `expr index "${text}" ll`

#查找 ll 子字符在 hello 字符串中的起始位置。
# Output:
# 3

数组

bash 只支持一维数组。数组下标从 0 开始,下标可以是整数或算术表达式,其值应大于或等于 0。

创建数组

# 创建数组的不同方式
nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")

访问数组元素

  • 访问数组的单个元素:
echo ${nums[1]}
# Output: 1
  • 访问数组的所有元素:
echo ${colors[*]}
# Output: red yellow dark blue

echo ${colors[@]}
# Output: red yellow dark blue
  • 访问数组的部分元素:
echo ${nums[@]:0:2}
# Output:
# 0 1

在上面的例子中,${array[@]} 扩展为整个数组,:0:2取出了数组中从 0 开始,长度为 2 的元素。

访问数组长度

echo ${#nums[*]}
# Output:
# 3

向数组中添加元素

colors=(white "${colors[@]}" green black)
echo ${colors[@]}
# Output:
# white red yellow dark blue green black

上面的例子中,${colors[@]} 扩展为整个数组,并被置换到复合赋值语句中,接着,对数组colors的赋值覆盖了它原来的值。

从数组中删除元素

unset命令来从数组中删除一个元素:

unset nums[0]
echo ${nums[@]}
# Output:
# 1 2

运算符

算术运算符

下表列出了常用的算术运算符,假定变量 x 为 10,变量 y 为 20:

运算符 说明 举例
+ 加法 expr $x + $y 结果为 30。
- 减法 expr $x - $y 结果为 -10。
* 乘法 expr $x * $y 结果为 200。
/ 除法 expr $y / $x 结果为 2。
% 取余 expr $y % $x 结果为 0。
= 赋值 x=$y 将把变量 y 的值赋给 x。
== 相等。用于比较两个数字,相同则返回 true。 [ $x == $y ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ $x != $y ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$x==$y] 是错误的,必须写成 [ $x == $y ]

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 x 为 10,变量 y 为 20:

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ]返回 false。
-ne 检测两个数是否相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ]返回 true。

布尔运算符

下表列出了常用的布尔运算符,假定变量 x 为 10,变量 y 为 20:

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 x 为 10,变量 y 为 20:

运算符 说明 举例
&& 逻辑的 AND [[ ${x} -lt 100 && ${y} -gt 100 ]] 返回 false
|| 逻辑的 OR [[ ${x} -lt 100 || ${y} -gt 100 ]] 返回 true

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg":

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为 0,为 0 返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否为 0,不为 0 返回 true。 [ -n $a ] 返回 true。
str 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ]返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于 0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

控制语句

条件语句

if

if在使用上跟其它语言相同。如果中括号里的表达式为真,那么thenfi之间的代码会被执行。fi标志着条件代码块的结束。

# 写成一行
if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi
# Output: 1 -eq 1 result is: true

# 写成多行
if [[ "abc" -eq "abc" ]]
then
  echo ""abc" -eq "abc" result is: true"
fi
# Output: abc -eq abc result is: true

if else

同样,我们可以使用if..else语句,例如:

if [[ 2 -ne 1 ]]; then
  echo "true"
else
  echo "false"
fi
# Output: true

if elif else

有些时候,当if..else不能满足我们的要求。还可以使用if..elif..else,例如:

x=10
y=20
if [[ ${x} > ${y} ]]; then
   echo "${x} > ${y}"
elif [[ ${x} < ${y} ]]; then
   echo "${x} < ${y}"
else
   echo "${x} = ${y}"
fi
# Output: 10 < 20

case

如果你需要面对很多情况,分别要采取不同的措施,那么使用case会比嵌套的if更有用。使用case来解决复杂的条件判断,例如:

exec
case ${oper} in
  "+")
    val=`expr ${x} + ${y}`
    echo "${x} + ${y} = ${val}"
  ;;
  "-")
    val=`expr ${x} - ${y}`
    echo "${x} - ${y} = ${val}"
  ;;
  "*")
    val=`expr ${x} \* ${y}`
    echo "${x} * ${y} = ${val}"
  ;;
  "/")
    val=`expr ${x} / ${y}`
    echo "${x} / ${y} = ${val}"
  ;;
  *)
    echo "Unknown oper!"
  ;;
esac

循环语句

Bash 中有四种循环:forwhileuntilselect

for循环

for arg in elem1 elem2 ... elemN
do
  ### 语句
done

在每次循环的过程中,arg依次被赋值为从elem1elemN。这些值还可以是通配符或者大括号扩展。 我们还可以把for循环写在一行,但这要求do之前要有一个分号,例如:

for i in {1..5}; do echo $i; done

也可以像 C 语言那样使用for,比如:

for (( i = 0; i < 10; i++ )); do
  echo $i
done

当我们想对一个目录下的所有文件做同样的操作时,for就很方便了。举个例子,如果我们想把所有的.bash文件移动到script文件夹中,我们的脚本可以这样写:

DIR=/home/xiaoming
for FILE in ${DIR}/*.sh; do
  mv "$FILE" "${DIR}/scripts"
done
# 将 /home/xiaoming 目录下所有 sh 文件拷贝到 /home/xiaoming/scripts

while循环

while循环检测一个条件,只要这个条件为 真,就执行一段命令。被检测的条件跟if..then中使用的基元并无二异。因此一个while循环看起来会是这样:

while [[ condition ]]
do
  ### 语句
done

for循环一样,如果我们把do和被检测的条件写到一行,那么必须要在do之前加一个分号。 比如下面这个例子:

### 0到9之间每个数的平方
x=0
while [[ ${x} -lt 10 ]]; do
  echo $((x * x))
  x=$((x + 1))
done
#  Output:
#  0
#  1
#  4
#  9
#  16
#  25
#  36
#  49
#  64
#  81

until循环

until循环跟while循环正好相反。它跟while一样也需要检测一个测试条件,但不同的是,只要该条件为 假 就一直执行循环:

x=0
until [[ ${x} -ge 5 ]]; do
  echo ${x}
  x=`expr ${x} + 1`
done
#  Output:
#  0
#  1
#  2
#  3
#  4

select in 循环

select in 循环用来增强交互性,它可以显示出带编号的菜单,用户输入不同的编号就可以选择不同的菜单,并执行不同的功能。

select in 是 Shell 独有的一种循环,非常适合终端(Terminal)这样的交互场景,C语言、C++、Java、Python、C# 等其它编程语言中是没有的。

Shell select in 循环的用法如下:

select variable in value_list
do
    statements
done

variable 表示变量,value_list 表示取值列表,in 是 Shell 中的关键字。你看,select in 和 for in 的语法是多么地相似。

我们先来看一个 select in 循环的例子:

#!/bin/bash
echo "What is your favourite OS?"
select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
do
    echo $name
done
echo "You have selected $name"

运行结果:

What is your favourite OS?
1) Linux
2) Windows
3) Mac OS
4) UNIX
5) Android
#? 4↙
You have selected UNIX
#? 1↙
You have selected Linux
#? 9↙
You have selected
#? 2↙
You have selected Windows
#?^D

#?用来提示用户输入菜单编号;^D表示按下 Ctrl+D 组合键,它的作用是结束 select in 循环。

运行到 select 语句后,取值列表 value_list 中的内容会以菜单的形式显示出来,用户输入菜单编号,就表示选中了某个值,这个值就会赋给变量 variable,然后再执行循环体中的 statements(do 和 done 之间的部分)。

每次循环时 select 都会要求用户输入菜单编号,并使用环境变量 PS3 的值作为提示符,PS3 的默认值为#?,修改 PS3 的值就可以修改提示符。

如果用户输入的菜单编号不在范围之内,例如上面我们输入的 9,那么就会给 variable 赋一个空值;如果用户输入一个空值(什么也不输入,直接回车),会重新显示一遍菜单。

注意,select 是无限循环(死循环),输入空值,或者输入的值无效,都不会结束循环,只有遇到 break 语句,或者按下 Ctrl+D 组合键才能结束循环。 完整实例 select in 通常和 case in 一起使用,在用户输入不同的编号时可以做出不同的反应。

修改上面的代码,加入 case in 语句:

#!/bin/bash
echo "What is your favourite OS?"
select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
do
    case $name in
        "Linux")
            echo "Linux是一个类UNIX操作系统,它开源免费,运行在各种服务器设备和嵌入式设备。"
            break
            ;;
        "Windows")
            echo "Windows是微软开发的个人电脑操作系统,它是闭源收费的。"
            break
            ;;
        "Mac OS")
            echo "Mac OS是苹果公司基于UNIX开发的一款图形界面操作系统,只能运行与苹果提供的硬件之上。"
            break
            ;;
        "UNIX")
            echo "UNIX是操作系统的开山鼻祖,现在已经逐渐退出历史舞台,只应用在特殊场合。"
            break
            ;;
        "Android")
            echo "Android是由Google开发的手机操作系统,目前已经占据了70%的市场份额。"
            break
            ;;
        *)
            echo "输入错误,请重新输入"
    esac
done

用户只有输入正确的编号才会结束循环,如果输入错误,会要求重新输入。

运行结果1,输入正确选项:

What is your favourite OS?
1) Linux
2) Windows
3) Mac OS
4) UNIX
5) Android
#? 2
Windows是微软开发的个人电脑操作系统,它是闭源收费的。

运行结果2,输入错误选项:

What is your favourite OS?
1) Linux
2) Windows
3) Mac OS
4) UNIX
5) Android
#? 7
输入错误,请重新输入
#? 4
UNIX是操作系统的开山鼻祖,现在已经逐渐退出历史舞台,只应用在特殊场合。

运行结果3,输入空值:

What is your favourite OS?
1) Linux
2) Windows
3) Mac OS
4) UNIX
5) Android
#?
1) Linux
2) Windows
3) Mac OS
4) UNIX
5) Android
#? 3
Mac OS是苹果公司基于UNIX开发的一款图形界面操作系统,只能运行与苹果提供的硬件之上。

break和continue

如果想提前结束一个循环或跳过某次循环执行,可以使用 shell 的breakcontinue语句来实现。它们可以在任何循环中使用。 break语句用来提前结束当前循环。 continue语句用来跳过某次迭代。

# 查找 10 以内第一个能整除 2 和 3 的正整数
i=1
while [[ ${i} -lt 10 ]]; do
  if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then
    echo ${i}
    break;
  fi
  i=`expr ${i} + 1`
done
# Output: 6
# 打印10以内的奇数
for (( i = 0; i < 10; i ++ )); do
  if [[ $((i % 2)) -eq 0 ]]; then
    continue;
  fi
  echo ${i}
done
#  Output:
#  1
#  3
#  5
#  7
#  9

函数

bash 函数定义语法如下:

[ function ] funname [()] {
    action;
    [return int;]
}

说明:

  1. 函数定义时,function 关键字可有可无。
  2. 函数返回值 - return 返回函数返回值,返回值类型只能为整数(0-255)。如果不加 return 语句,shell 默认将以最后一条命令的运行结果,作为函数返回值。
  3. 函数返回值在调用该函数后通过 $? 来获得。
  4. 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
#!/usr/bin/env bash

calc(){
  PS3="choose the oper: "
  select oper in + - \* / # 生成操作符选择菜单
  do
  echo -n "enter first num: " && read x # 读取输入参数
  echo -n "enter second num: " && read y # 读取输入参数
  exec
  case ${oper} in
    "+")
      return $((${x} + ${y}))
    ;;
    "-")
      return $((${x} - ${y}))
    ;;
    "*")
      return $((${x} * ${y}))
    ;;
    "/")
      return $((${x} / ${y}))
    ;;
    *)
      echo "${oper} is not support!"
      return 0
    ;;
  esac
  break
  done
}
calc
echo "the result is: $?" # $? 获取 calc 函数返回值

执行结果:

$ ./function-demo.sh
1) +
2) -
3) *
4) /
choose the oper: 3
enter first num: 10
enter second num: 10
the result is: 100

位置参数

位置参数是在调用一个函数并传给它参数时创建的变量。

位置参数变量表:

变量 描述
$0 脚本名称
$1 … $9 第 1 个到第 9 个参数列表
${10} … ${N} 第 10 个到 N 个参数列表
$* or $@ 除了$0外的所有位置参数
$# 不包括$0在内的位置参数的个数
$FUNCNAME 函数名称(仅在函数内部有值)
#!/usr/bin/env bash

x=0
if [[ -n $1 ]]; then
  echo "第一个参数为:$1"
  x=$1
else
  echo "第一个参数为空"
fi

y=0
if [[ -n $2 ]]; then
  echo "第二个参数为:$2"
  y=$2
else
  echo "第二个参数为空"
fi

paramsFunction(){
  echo "函数第一个入参:$1"
  echo "函数第二个入参:$2"
}
paramsFunction ${x} ${y}

执行结果:

$ ./function-demo2.sh
第一个参数为空
第二个参数为空
函数第一个入参:0
函数第二个入参:0

$ ./function-demo2.sh 10 20
第一个参数为:10
第二个参数为:20
函数第一个入参:10
函数第二个入参:20

执行 ./variable-demo4.sh hello world ,然后在脚本中通过 $1$2 ... 读取第 1 个参数、第 2 个参数。。。

函数处理参数

另外,还有几个特殊字符用来处理参数:

参数处理 说明
$# 返回参数个数
$* 返回所有参数
? 脚本运行的当前进程 ID 号
$! 后台运行的最后一个进程的 ID 号
$@ 返回所有参数
$- 返回 Shell 使用的当前选项,与 set 命令功能相同。
$? 函数返回值
runner() {
  return 0
}

name=xiaoming
paramsFunction(){
  echo "函数第一个入参:$1"
  echo "函数第二个入参:$2"
  echo "传递到脚本的参数个数:$#"
  echo "所有参数:"
  printf "+ %s\n" "$*"
  echo "脚本运行的当前进程 ID 号:?"
  echo "后台运行的最后一个进程的 ID 号:$!"
  echo "所有参数:"
  printf "+ %s\n" "$@"
  echo "Shell 使用的当前选项:$-"
  runner
  echo "runner 函数的返回值:$?"
}
paramsFunction 1 "abc" "hello, \"xiaoming\""
#  Output:
#  函数第一个入参:1
#  函数第二个入参:abc
#  传递到脚本的参数个数:3
#  所有参数:
#  + 1 abc hello, "xiaoming"
#  脚本运行的当前进程 ID 号:26400
#  后台运行的最后一个进程的 ID 号:
#  所有参数:
#  + 1
#  + abc
#  + hello, "xiaoming"
#  Shell 使用的当前选项:hB
#  runner 函数的返回值:0