为什么回声会忽略我的引号字符?


24

echo命令未包含我提供的完整文本。例如,如果我这样做:

   $ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

它输出:

echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `

'我的echo命令中的单引号()不包括在内。如果我改用双引号:

$ echo " echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `  "

echo根本不输出任何东西!为什么在第一个示例中省略单引号而在第二个示例中不输出任何内容?如何获得与输入内容完全相同的输出?


3
不太确定您要在这里实现什么。为什么将一个echo语句嵌套在另一个中?还是您实际上是想打印出命令(例如示例等)?
安迪·史密斯

我需要将回声输出插入另一个文件

您需要说明您要做什么。如“我要在文件后附加以下文本:'...'。”
MikeyB 2011年

2
我已经尝试编辑了5分钟,但我真的不知道所需的输出是什么。编辑:好的,我想我现在有一个猜测,所以我尝试进行编辑。如果不是您想要的,请告诉我
Michael Mrozek

1
@Michael-哇-如果我可以给您一些编辑意见,我会-做得很好,这是一个非常好的问题!
兰德尔

Answers:


33

您的shell会在甚至到达之前解释引号('和)。我通常只是在引号周围加上双引号,以使它们回声,即使它们是不必要的也是如此。例如:"echo

$ echo "Hello world"
Hello world

因此,在第一个示例中,如果要在输出中包括文字引号,则需要对它们进行转义:

$ echo \'Hello world\'
'Hello world'

或者它们需要已经在带引号的参数中使用了(但是不能是相同类型的引号,或者无论如何都需要转义):

$ echo "'Hello world'"
'Hello world'

$ echo '"Hello world"'
"Hello world"

在第二个示例中,您正在字符串中间运行命令替换:

grep  $ARG  /var/tmp/setfile  | awk {print $2}

开头$的内容也由shell专门处理-将它们视为变量并将其替换为它们的值。由于最有可能在您的外壳中未设置这些变量,因此实际上它只是在运行

grep /var/tmp/setfile | awk {print}

由于grep仅看到一个参数,因此假定该参数是您要搜索的模式,并且应从中读取数据的位置是stdin,因此它将阻止等待输入。这就是为什么您的第二个命令似乎挂起的原因。

如果单引号将不会发生(这就是第一个示例几乎起作用的原因),因此这是获取所需输出的一种方法:

echo \'' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '\'

您也可以将其双引号,但随后需要转义$s,以便外壳程序不将其解析为变量,而将反引号转义,因此外壳程序不会立即运行命令替换:

echo "' echo PARAM=\`  grep  \$ARG  /var/tmp/setfile  | awk '{print \$2}' \`    '"

7

我不会详细说明您的尝试为何会如此行事,因为Michael Mrozek的回答很好地说明了这一点。简而言之,单引号之间的所有内容('…')字面上(尤其是第一个解释'标记文本字符串的结尾),而`$保留之间仍然具有特殊意义"…"

单引号内没有引号,因此您不能在单引号字符串内放置单引号。但是,有一个看起来像这样的成语:

echo 'foo'\''bar'

这将打印出来foo'bar,因为to的参数echo由单引号三字符字符串组成foo,并与单字符'(通过'从前面的特殊字符保护字符获得\)并与单引号三字符字符串相连bar。因此,尽管幕后情况并不太严重,但是您可以考虑'\''将单引号包含在单引号字符串中。

如果您要打印复杂的多行字符串,那么这里文档是一个更好的工具。这里的文档由两个字符组成,<<后跟一个标记(例如)<<EOF,然后是一些文本行,然后是其行尾的结束标记。如果标记以任何方式加引号('EOF'"EOF"\EOF或'E“” OF“或…),则文本将按字面意义进行解释(就像单引号内一样,只是偶数'是普通字符)。如果标记根本未加引号,则将文本解释为双引号字符串,并$\`保留其特殊状态(但"换行符按字面意义解释)。

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF

1

好的,这将起作用:

echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

在这里测试:

[asmith@yagi ~]$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '
 echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ` 

0

Gilles关于使用here文档的建议非常好,甚至可以在Perl之类的脚本语言中使用。作为一个具体示例,基于他对OP问题的回答,

use strict;
my $cmd = q#cat <<'EOF' # . "\n" .
          q#echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `# . "\n" .
          q#EOF# ;
open (my $OUTPUT, "$cmd |");
print $_ while <$OUTPUT>;
close $OUTPUT;

当然,这个示例有些人为,但是我发现它是一种有用的技术,例如,将SQL语句发送到psql(而不是cat)。

(请注意,可以使用任何非字母数字的非空格字符代替#上面的通用引用构造中的,但是在该示例中,散列似乎很突出,并且被视为注释。)


-1

让我们接受您的命令

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

然后首先使用unquoted空白作为分隔符将其拆分为单词:

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“$2}' `    '”

接下来,我们进行参数扩展:扩展$…但不在inside '…'。由于$2为空(除非您通过eg设置set -- …),它将被一个空字符串替换。

Arg[1]=“' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“}' `    '”

接下来,进行报价删除。

Arg[1]=“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print”
Arg[2]=“} `    ”

这些参数然后传递给echo,它将逐个打印它们,并用一个空格隔开。

“ echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `    ”

我希望此分步说明可以使观察到的行为更清晰。为避免此问题,请使用单引号将此类转义:

$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

这将结束单引号的字符串,然后在单词上添加一个(转义的)单引号,然后开始一个新的单引号字符串,所有操作都不会将单词分开。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.