如何在执行命令之前回显命令行?

3

我需要执行命令,并且需要显示我已经执行的命令。因此,我一直在尝试将命令存储在变量中并执行该变量。在显示输出时,我会像下面一样打印变量值。

var="ls -ltr";
echo "$var \n `$var`";

上面的代码输出了我想要的结果,但是当我使用下面提到的命令时,会出现错误。有人能建议我如何存储下面提到的命令吗?

var="ls -ltr|wc -l";
echo "$var \n `$var`";

输出错误:

ls -ltr | wc -l
ls: cannot access |: No such file or directory
ls: cannot access wc: No such file or directory

如何正确地将以下命令存储在变量中?或者给我任何替代建议以满足我的要求。

cat passwd.txt|tr ' ' '\n'|egrep -i "$pwd_in"|sort

5
强制性链接:http://mywiki.wooledge.org/BashFAQ/050 - Tom Fenech
1
真的要仔细阅读上面的链接。将命令存储在变量中,在几乎所有情况下都是错误的想法... - clt60
@tripleee:这个问题是想在执行命令之前回显任意命令行。链接的问题是关于为什么由shell扩展产生的引号没有语法意义。虽然它们有一定的关系,但它们不是重复的。 - mklement0
1
好的,感谢您的反馈。为了历史背景,我建议作为重复的问题是https://dev59.com/Kmct5IYBdhLWcg3wXsbH - tripleee
4个回答

4

将您的命令放在一个函数中,并启用xtrace

cmd ()
{
    set -x
    ls -ltr | wc -l
    set +x
}

3
你的方法不起作用的原因是shell元字符(例如|)在字符串中被嵌入时会失去其语法意义这个相关问题 的答案解释了为什么;那个问题涉及到嵌入的引号,但答案仍然类似适用。
此外,通常不建议将命令存储在变量中; 简而言之:
  • 除非使用eval(不建议),否则仅适用于简单命令(不由控制运算符如|&&组成的多个命令),
  • 并且仅当您将命令的参数存储在数组中时才能够稳健地工作。
为了得到您想要的结果,无论如何都需要使用eval函数,但是这种做法一般不被推荐出于安全原因
  • 简而言之:只有在完全控制或信任传递给eval的字符串时,才使用eval;也就是说,仅当使用变量$vareval "$var")时,如果且仅如果
    • 完全控制分配给$var的值,知道它是一个命令,并知道它按预期工作(即使部分值由外部来源提供,下一个点也适用)
    • 或者信任$var值的任何外部贡献的来源(无论它是否完全或部分形成$var的值)- 特别是,您需要确保不会尝试恶意提供/注入不需要的命令。
最可预测的基于 eval 的解决方案如下,它比 AxT_8041's answer 更好(适用于 bashksh):
注意:由于此命令是由您构建的字符串文字,因此在此情况下接受使用 eval
cmdLine='ls -ltr|wc -l'
printf "%s\n" "$cmdLine"; eval "$cmdLine"

引用更复杂的命令行变得困难,这时您可以使用一个“here-document”:
# Read the command into variable $cmdLine.
read -d '' -r cmdLine <<'EOF'
cat passwd.txt|tr ' ' '\n'|egrep -i "$pwd_in"|sort
EOF
printf "%s\n" "$cmdLine"; eval "$cmdLine"

注意:虽然此命令也是由您构建的,但它是通过合并变量$pwd_in的值而形成的,因此为了在此安全使用eval,您必须“了解或信任”$pwd_in具有预期类型的值,特别是它不包含尝试“注入命令”的内容。
一种安全但有限的解决方案使用shell提供的诊断功能

对于bashset -v会自动在执行每个原始命令行之前回显它(无需首先在变量中定义感兴趣的命令):

set -v            # Turn echoing of raw command lines on.
ls -ltr | wc -l   # Execute the command of interest.
set +v            # Turn back off.

例如,这将产生以下结果:
ls -ltr | wc -l
15
set +v

然而,存在一些限制:
- Bash回显的原始命令行被发送到stderr。 - 请注意,ceving的答案中的`set -x`是一个相关的功能,但它的工作方式不同:它将单独地以已经扩展的形式回显组成管道的简单命令(例如`ls -ltr`和`wc -l`)。 - 据我所知,无法抑制`set +v`输出行。 你可以尝试解决这些问题,但这并不容易或美观。

0

如果速度不是问题,值得考虑的奖金选项:

(set -x; ls -l "$PWD")

这将生成一个子shell,继承父shell的所有属性,但好处是您不必担心在要打印的每个语句结尾处使用set +x进行清理。


0

以下是在Bash上工作的。请尝试:

var="ls -ltr|wc -l";
echo -e "$var \n `eval $var`";

Output:
ls -ltr|wc -l 
10

你存储在$var中了什么内容,以及如何存储的? - Sriram P
你所存储的东西是相同的。 - toxic_boi_8041
1
@SriramP 它的工作原理在于使用 eval,它将字符串作为命令行进行评估。 - JeremyP
2
在没有通常的警告的情况下推荐使用eval并将其隐藏在echo -e的反引号中,你可能会引发一些意想不到的行为或后果,因此会被贬低。 - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接