跳转到内容

Bash Shell 脚本/Shell 函数

来自维基教科书,自由的教科书

Shell 函数是一种特殊类型的变量,本质上是脚本中的脚本。此功能允许我们将一系列命令分组到单个命名命令中,这在需要在脚本中的多个位置运行一系列命令时特别有用。Shell 函数甚至可以仅包含单个命令;如果命令特别复杂,或者其重要性不会立即对读者显而易见,这可能很有用。(也就是说,Shell 函数可以服务于两个目的:它们可以节省输入,并且可以通过创建直观的命名命令来使代码更易读。)请考虑以下脚本

#!/bin/bash
# Usage:     get_password VARNAME
# Asks the user for a password; saves it as $VARNAME.
# Returns a non-zero exit status if standard input is not a terminal, or if the
# "read" command returns a non-zero exit status.
get_password() {
  if [[ -t 0 ]] ; then
    read -r -p 'Password:' -s "$1" && echo
  else
    return 1
  fi
}

get_password PASSWORD && echo "$PASSWORD"

以上脚本创建了一个名为get_password的 Shell 函数,它要求用户输入密码并将结果存储在指定的变量中。然后它运行get_password PASSWORD 将密码存储为$PASSWORD;最后,如果对get_password的调用成功(由其退出状态决定),则检索到的密码将被打印到标准输出(这显然不是实际用途;这里的目标只是为了演示get_password的行为)。

函数get_password没有做任何在没有 Shell 函数的情况下做不到的事情,但结果更易读。该函数调用内置命令read(它读取一行用户输入并将其保存在一个或多个变量中)以及大多数 Bash 程序员不熟悉的一些选项。(-r 选项禁用反斜杠字符的特殊含义;-p 选项导致在行首显示指定的提示,在本例中为Password:-s 选项阻止用户键入的密码显示。由于-s 选项还会阻止用户的新行显示,因此echo 命令提供了一个新行。)此外,该函数使用条件表达式-t 0来确保脚本的输入来自终端(控制台),而不是来自文件或另一个不知道正在请求密码的程序。(这个最后的特性是可争议的;根据脚本的总体功能,最好接受来自标准输入的密码,无论其来源如何,假设源是考虑到脚本而设计的。)总的来说,给一系列命令命名——get_password——使程序员更容易了解它所做的事情。

在 Shell 函数中,位置参数($1$2 等等,以及$@$*$#)是指调用函数时传递的参数,而不是包含该函数的脚本的参数。如果需要后者,则需要使用"$@" 显式地将它们传递给函数。(即使那样,shiftset 也会只影响函数中的位置参数,而不是调用者的位置参数。)

函数调用会返回退出状态,就像脚本(或几乎任何命令)一样。要显式指定退出状态,请使用return 命令,它会终止函数调用并返回指定的退出状态。(exit 命令不能用于此目的,因为它会终止整个脚本,就像在函数外部调用它一样。)如果未指定退出状态,无论是由于未向return 命令提供任何参数,还是由于在未运行return 命令的情况下到达函数的末尾,则函数将返回上次运行命令的退出状态。

顺便说一句,function( ) 可以从函数声明中省略,但必须至少存在一个。许多程序员编写get_password(),而不是function get_password ( )。类似地,{ … } 符号并不是严格要求的,也不是特定于函数的;它只是将一系列命令组合成单个复合命令的符号。函数的主体必须是复合命令,例如{ … } 块或if 语句;{ … } 是传统的选择,即使它只包含一个复合命令,并且理论上可以省略。

华夏公益教科书