当运行函数定义和调用函数时,以下哪些 shell 操作是在函数体内执行的?

当运行函数定义和调用函数时,以下哪些 shell 操作是在函数体内执行的?

函数定义是一个命令。当函数定义运行时,我认为函数体将保持完整,就像函数体是单引号一样。

当我从以下内容中了解到以下内容时,我知道我错了bash手册G-Man的回答 给我的别名和函数问题:

别名在读取函数定义时展开,而不是在执行函数时展开,……。

所以别名扩展是在函数体中进行的,而参数扩展则不是,如下例所示:

$ alias myalias=cat
$ echo $var
3
$ myfunc() { myalias myfile; echo $var; }
$ declare -fp myfunc
myfunc () 
{ 
    cat myfile;
    echo $var
}

相似地,对函数的调用也是一个命令。

函数中定义的别名只有在执行该函数之后才可用。

因此别名定义仅在执行时(即调用函数时)执行,而不是在运行函数定义时执行。

别名扩展和别名定义执行只是下面列出的 shell 操作中的两个。

我的问题是:

  1. 函数体内执行了哪些shell操作,不执行哪些操作,

    • 什么时候运行函数定义

    • 什么时候调用函数, IE,执行一个函数

  2. 在运行函数定义和调用函数期间是否执行任何 shell 操作?

    或者运行函数的定义和调用该函数是否执行不重叠的 shell 操作集?

以下引用中列出了可能的操作bash手册

3.1.1 外壳操作

下面简单描述一下shell读取并执行命令时的操作。基本上,shell 执行以下操作:

  1. 它的输入来自文件(参见第 3.8 节 [Shell 脚本],第 39 页)、作为调用选项的参数提供的字符串-c(参见第 6.1 节 [调用 Bash],第 80 页)或来自用户终端。

  2. 休息时间输入到单词和运算符中,遵守第 6 页第 3.1.2 节 [引用] 中描述的引用规则。这些标记由元字符分隔。别名扩展通过此步骤执行(参见第 6.6 节 [别名],第 88 页)。

  3. 解析将标记转换为简单命令和复合命令(请参阅第 3.2 节 [Shell 命令],第 8 页)。

  4. 执行各种外壳扩展(请参见第 3.5 节 [Shell 扩展],第 21 页),将扩展标记分解为文件名列表(请参见第 3.5.8 节 [文件名扩展],第 30 页)以及命令和参数。

  5. 执行任何必要的操作重定向(请参见第 31 页第 3.6 节 [重定向])并从参数列表中删除重定向运算符及其操作数。

  6. 执行命令(参见第 35 页第 3.7 节 [执行命令])。

  7. 可选等待命令完成并收集其退出状态(请参阅第 38 页第 3.7.5 节 [退出状态])。

答案1

<rant>
“运行函数定义”和“运行函数的定义”不是常见的习惯用法。我预计大多数人会将其简单地称为“定义函数”。好的,该短语可能被解释为指的是

  1. 从概念上指定功能(这是一个人或一群人执行的活动)的特征和接口(参数和其他输入、处理和输出)
  2. 选择函数的实现;即在函数体中使用什么命令。这是由一个人或一群人可能使用纸张、白板和/或黑板进行的活动。
  3. 打字将函数实现到文本编辑器或直接交互式 shell 中(当然,这是由一个人,或者可能是一群人,或者可能是非常聪明和灵巧的猫、狗或猴子进行的活动)

如果您担心与上述内容混淆,那么“阅读函数定义”和“处理函数定义”等短语可能会更好。

你说,“函数定义就是命令。”目前还不清楚这样的说法意味着什么——这是一个语义问题——但是,在语义层面上,这是值得商榷的。  Bash 手册第 3.2 节(Shell 命令) 列出了六种 shell 命令:简单命令、管道、列表、复合命令、协进程和 GNU 并行。函数定义不太适合这些类别中的任何一个。
</咆哮>

回答你的问题,看看七个步骤/操作,

  1. 读取输入显然必须发生在之前任何事物否则可能会发生。这又是一个语义问题——你可以说从文件或其他流文本源读取输入是“读取函数定义”的一部分,或者说它是“处理函数定义”的先决条件/前奏。
  2. 将输入分解为单词和运算符显然是“处理函数定义”的一部分。
    • 当然,将输入分解为令牌是先决条件/前奏解析令牌(下面的步骤 3)。
    • 手册中说,“别名扩展是通过此步骤执行的”,并且您在问题中表明您知道在读取函数定义时会发生别名扩展。
  3. 解析标记。这必须至少在“处理函数定义”阶段开始,以便 shell 能够意识到它正在查看函数定义而不是简单的命令。除此之外,我们可以做这个简单的实验:在 shell 中输入以下任一内容:

    myfunc1() {                     myfunc2() {
        <               or              ;
    }                               }
    

    在您有机会输入}. (>&产生相同的效果。)很明显,这一步是处理函数定义的一部分。


  1. 执行各种 shell 扩展是调用函数的一部分。
    • 您在问题中表明您知道参数扩展才不是发生 当读取函数定义时。
    • 证明读取函数定义时不会发生路径名/文件名扩展也同样微不足道。在您的示例中,将echo $var (应该是echo "$var",但那是另一回事)更改为echo *
      • 如果您查看函数定义,您会发现它仍然显示echo *并且尚未扩展。
      • 更改目录 ( cd) 和/或创建文件和/或删除文件和/或重命名文件,然后调用(执行)该函数。你会看到它输出当前的当前目录中的文件1的列表 ,并且不反映定义函数时/所在目录的内容。
  2. 执行重定向是调用该函数的一部分。这也很容易验证。在您的示例中,更改echo $varecho hello > myfile.
    • 如果您查看函数定义,您会发现它仍然显示为 > myfile
    • 检查目录,您会发现它myfile尚未创建。
  3. 执行命令——你真的需要问吗?
  4. 在执行命令(步骤 6)之前不可能等待命令完成,因此显然只有在调用函数时才执行此操作。

换句话说,正如@ilkkachu所说

我能想到的一个例外的特殊情况是,如果函数包含eval语句,则在执行函数时(再次)发生步骤 2 和 3(词法分析和解析)——但我相信这超出了您的问题真正要问的范围。
____________
1不包括隐藏(点)文件,除非您已说明shopt -s dotglob

相关内容