函数定义是一个命令。当函数定义运行时,我认为函数体将保持完整,就像函数体是单引号一样。
当我从以下内容中了解到以下内容时,我知道我错了bash手册 在G-Man的回答 给我的别名和函数问题:
别名在读取函数定义时展开,而不是在执行函数时展开,……。
所以别名扩展是在函数体中进行的,而参数扩展则不是,如下例所示:
$ alias myalias=cat
$ echo $var
3
$ myfunc() { myalias myfile; echo $var; }
$ declare -fp myfunc
myfunc ()
{
cat myfile;
echo $var
}
相似地,对函数的调用也是一个命令。
函数中定义的别名只有在执行该函数之后才可用。
因此别名定义仅在执行时(即调用函数时)执行,而不是在运行函数定义时执行。
别名扩展和别名定义执行只是下面列出的 shell 操作中的两个。
我的问题是:
函数体内执行了哪些shell操作,不执行哪些操作,
什么时候运行函数定义?
什么时候调用函数, IE,执行一个函数?
在运行函数定义和调用函数期间是否执行任何 shell 操作?
或者运行函数的定义和调用该函数是否执行不重叠的 shell 操作集?
以下引用中列出了可能的操作bash手册:
3.1.1 外壳操作
下面简单描述一下shell读取并执行命令时的操作。基本上,shell 执行以下操作:
读它的输入来自文件(参见第 3.8 节 [Shell 脚本],第 39 页)、作为调用选项的参数提供的字符串
-c
(参见第 6.1 节 [调用 Bash],第 80 页)或来自用户终端。休息时间输入到单词和运算符中,遵守第 6 页第 3.1.2 节 [引用] 中描述的引用规则。这些标记由元字符分隔。别名扩展通过此步骤执行(参见第 6.6 节 [别名],第 88 页)。
解析将标记转换为简单命令和复合命令(请参阅第 3.2 节 [Shell 命令],第 8 页)。
执行各种外壳扩展(请参见第 3.5 节 [Shell 扩展],第 21 页),将扩展标记分解为文件名列表(请参见第 3.5.8 节 [文件名扩展],第 30 页)以及命令和参数。
执行任何必要的操作重定向(请参见第 31 页第 3.6 节 [重定向])并从参数列表中删除重定向运算符及其操作数。
执行命令(参见第 35 页第 3.7 节 [执行命令])。
可选等待命令完成并收集其退出状态(请参阅第 38 页第 3.7.5 节 [退出状态])。
答案1
<rant>
“运行函数定义”和“运行函数的定义”不是常见的习惯用法。我预计大多数人会将其简单地称为“定义函数”。好的,该短语可能被解释为指的是
- 从概念上指定功能(这是一个人或一群人执行的活动)的特征和接口(参数和其他输入、处理和输出)
- 选择函数的实现;即在函数体中使用什么命令。这是由一个人或一群人可能使用纸张、白板和/或黑板进行的活动。
- 打字将函数实现到文本编辑器或直接交互式 shell 中(当然,这是还由一个人,或者可能是一群人,或者可能是非常聪明和灵巧的猫、狗或猴子进行的活动)
如果您担心与上述内容混淆,那么“阅读函数定义”和“处理函数定义”等短语可能会更好。
你说,“函数定义就是命令。”目前还不清楚这样的说法意味着什么——这是一个语义问题——但是,在语义层面上,这是值得商榷的。 Bash 手册第 3.2 节(Shell 命令) 列出了六种 shell 命令:简单命令、管道、列表、复合命令、协进程和 GNU 并行。函数定义不太适合这些类别中的任何一个。
</咆哮>
回答你的问题,看看七个步骤/操作,
- 读取输入显然必须发生在之前任何事物否则可能会发生。这又是一个语义问题——你可以说从文件或其他流文本源读取输入是“读取函数定义”的一部分,或者说它是“处理函数定义”的先决条件/前奏。
- 将输入分解为单词和运算符显然是“处理函数定义”的一部分。
- 当然,将输入分解为令牌是先决条件/前奏解析令牌(下面的步骤 3)。
- 手册中说,“别名扩展是通过此步骤执行的”,并且您在问题中表明您知道在读取函数定义时会发生别名扩展。
解析标记。这必须至少在“处理函数定义”阶段开始,以便 shell 能够意识到它正在查看函数定义而不是简单的命令。除此之外,我们可以做这个简单的实验:在 shell 中输入以下任一内容:
myfunc1() { myfunc2() { < or ; } }
在您有机会输入
}
. (>
并&
产生相同的效果。)很明显,这一步是处理函数定义的一部分。
- 执行各种 shell 扩展是调用函数的一部分。
- 您在问题中表明您知道参数扩展才不是发生 当读取函数定义时。
- 证明读取函数定义时不会发生路径名/文件名扩展也同样微不足道。在您的示例中,将
echo $var
(应该是echo "$var"
,但那是另一回事)更改为echo *
。- 如果您查看函数定义,您会发现它仍然显示
echo *
并且尚未扩展。 - 更改目录 (
cd
) 和/或创建文件和/或删除文件和/或重命名文件,然后调用(执行)该函数。你会看到它输出当前的当前目录中的文件1的列表 ,并且不反映定义函数时/所在目录的内容。
- 如果您查看函数定义,您会发现它仍然显示
- 执行重定向是调用该函数的一部分。这也很容易验证。在您的示例中,更改
echo $var
为echo hello > myfile
.- 如果您查看函数定义,您会发现它仍然显示为
> myfile
。 - 检查目录,您会发现它
myfile
尚未创建。
- 如果您查看函数定义,您会发现它仍然显示为
- 执行命令——你真的需要问吗?
- 在执行命令(步骤 6)之前不可能等待命令完成,因此显然只有在调用函数时才执行此操作。
换句话说,正如@ilkkachu所说。
我能想到的一个例外的特殊情况是,如果函数包含eval
语句,则在执行函数时(再次)发生步骤 2 和 3(词法分析和解析)——但我相信这超出了您的问题真正要问的范围。
____________
1不包括隐藏(点)文件,除非您已说明shopt -s dotglob