如何在 Bash 读取完整的输入行之后但执行该行上的任何命令之前显示结果?

如何在 Bash 读取完整的输入行之后但执行该行上的任何命令之前显示结果?

来自Bash 手册,第 6.6 节别名,

  … 总是猛击之前至少输入一整行 执行该行上的任何命令。当执行命令时别名会扩展,不是当它是 被处决。因此,与另一个命令出现在同一行的别名定义在读取下一行输入之前不会生效。

在这里我试图找到一种理解的方式 当“读取整行输入”和“执行该行中的任何命令”时,shell 执行以下哪些操作?

有没有一些命令和例子可以显示结果

  • 重击 一整行输入
  • 执行该行有任何命令吗?

例如,

  • “一整行输入”是由多个命令组成的复合命令。
  • “一个完整的输入行”也可以是多个复合命令的管道、管道列表等。

答案1

理解这一点的一个好方法是看一下这个例子:https://stackoverflow.com/questions/38526612/bash-unable-to-set-and-use-alias-in-the-same-line

例如,您要执行以下命令:

$ alias foo="echo bac" ; foo;
-bash: foo: command not found
$ foo
bac

现在 bash 将读取整行,并且由于foo没有别名,因此它不会扩展(别名在读取时扩展)。现在,在执行该行时,它将设置别名并引发错误。

答案2

你引用的 Bash 手册的部分谈到

别名在读取命令时展开,而不是在执行命令时展开。因此,与另一个命令出现在同一行的别名定义在读取下一行输入之前不会生效。

因此,别名实际上是显示读取和执行一行之间差异的示例之一!

为了澄清这一点,这里有一个复合命令。我们首先尝试定义一个别名,然后尝试使用它:

alias echo=uname; echo

不熟悉 Bash 的人可能会期望该命令的输出是Linux(或您正在运行的任何操作系统),但实际上它什么也不输出。这是因为 Bash 首先该行并应用别名定义。这就是 Bash 看到的:

alias echo=uname; echo

请注意,第二个echo不会转换为uname.这是因为 Bash 还没有执行我们的alias echo=uname命令,它只执行了它。 Bash 读取该行后,会执行所读取的内容。因此它执行:

alias echo=uname; echo(这正是它所读到的,如上所述)

我们甚至可以通过输入来检查 Bash 是否执行了命令echo。由于我们之前定义了一个别名,现在将在读取步骤中echo转换为,因此 Bash 将执行.unameuname

简而言之:

$ alias echo=uname; echo

$ echo
Linux

编辑:

编辑后,您询问是否有一种方法可以在 Bash 读取一行输入之后、实际执行它们之前显示结果。答案是肯定的,但是对于日常使用来说并不是很方便(但是,它对于理解 shell 内部发生的情况肯定很有用)。

创建一个包含以下内容的文件:

#!/bin/bash
shopt -s expand_aliases
trapped() {
    echo "On line ${1}, Bash sees: $BASH_COMMAND"
}
trap 'trapped ${LINENO}' DEBUG
alias echo=uname; echo
echo

然后,执行该文件。

以下是该脚本如何工作的解释:

  • shopt -s expand_aliases告诉 Bash 扩展别名,即使它处于非交互模式(例如在本例中)
  • 然后我们定义一个函数trapped()。调用时,此函数将打印当前执行的命令(以及作为参数传递的行号)。
  • 我们称之为trap 'trapped ${LINENO}' DEBUG。这告诉 Bash“每当你看到一个名为 的信号时DEBUGtrapped ${LINENO}首先执行。DEBUG这是一个由 Bash 自动生成的信号在执行任何简单命令之前。换句话说,在执行任何命令之前,我们的函数trapped()都会被调用。
  • 最后,我们执行一些命令进行演示。然而,由于我们有一个陷阱,trapped()它会在其他事情发生之前执行,所以 Bash 会打印出即将执行的内容。该LINENO变量只是 Bash 内置变量,它为我们提供当前行号。

如果您希望能够“运行”命令而不执行它们,并且仍然看到 Bash 读取的内容,您可能需要查看extdebugBash 手册中的选项。

答案3

你的问题不是很清楚,但是你的句子并置

当命令,不是当它是被处决

总是猛击之前至少输入一整行 执行该行上的任何命令。

表明您问题的焦点是别名扩展。 (我说“建议”是因为,除了 Bash 手册摘录之外,你的问题根本没有提到别名。)

您提出的问题的原始版本

如何证明 Bash 在执行该行上的任何命令之前始终至少读取完整的一行输入?

执行此操作的唯一真正方法是使用某种调试/监视/跟踪工具来检查 shell 的内部状态。这有点不切实际。  小企鹅的回答展示了一种通过手动论证来支持手册内容的方法。这是他们的答案的一个变体(IMO)更清晰一些:

$ 别名 myalias=日期

$ 别名 myalias="ls -ld";别名
2017 年 8 月 4 日星期五 3:33:33 上午

我们可以说,shell 在开始执行之前必须完整地读取(第二个)完整行,因为当它执行时myalias,它将作为 执行date。当myalias执行别名时,它已被重新定义为ls -ld.因此,shell 在执行之前必须已经扩展myaliasdate

alias myalias="ls -ld"

命令,因此它必须在执行该行上的任何命令之前读取完整的输入行。


您修改后的问题,

如何在 Bash 读取完整的输入行之后但执行该行上的任何命令之前显示结果?

在这种情况下更有意义。我猜你想看看命令行别名已扩展  在执行该行上的任何命令之前;即查看别名扩展的结果。我猜你想看到类似的东西

alias myalias="ls -ld"; date

据我所知,没有任何方法可以获得该输出 - 扩展了别名的完整行。但xtrace( -x) 选项很接近。

$设置-x

$ 别名 myalias=日期
+ 别名 myalias=日期

$ 别名 myalias="ls -ld";别名
+ 别名 'myalias=ls -ld'
+ 日期                                                         ← 别名的扩展
2017 年 8 月 4 日星期五 3:33:42 上午

当然,这仅在执行每个扩展命令之前输出它,因此别名扩展的结果与命令的输出混合在一起。另外,这表明全部扩展:

$动物=猫
+ 动物=猫

$回显你好;回响世界; myalias "$animal" R*
+ 回显你好
你好
+ 回响世界
世界
+ ls -ld cat 自述文件                                            ← 别名的扩展
ls: 无法访问 cat: 没有这样的文件或目录
-rw-r--r-- 2我的用户名 我的组名  489 2015 年 2 月 22 日自述文件

我们看不到ls -ld "$animal" R*;我们只有在变量扩展和路径名(glob)扩展之后才能看到它。它也不能与管道“很好地配合”:

$ 别名 myalias=日期
+ 别名 myalias=日期

$ 别名 | OD-CB
+ 日期                                                         ← 别名的扩展
+ od -cb
0000000 周五、八月 0 4 、 2 0 1
        106 162 151 054 040 101 165 147 040 060 064 054 040 062 060 061
0000020 7 9 : 4 2 : 1 下午 7 点 \n
        067 040 040 071 072 064 062 072 061 067 040 120 115 012
0000036

跟踪输出在两条单独的行上向我们显示了date和。 (如果从输出中看不出来,od -cb您需要查看原始命令行来弄清楚它正在做什么。)date | od -cb

相关内容