为什么 bash 脚本中 awk 的 echo 会删除换行符?

为什么 bash 脚本中 awk 的 echo 会删除换行符?

我试图在 bash 脚本中包含一些 awk 命令,但遇到了一些意外的行为。你能给我一个关于我所忽略的线索吗?

例如,给定一个名为list

1
2
3
4

这个简单的awk命令可以实现您所期望的功能:

$ awk -F, '{ print $1 }' list
1
2
3
4

但如果我将此命令放入 bash 脚本中:

#!/bin/bash
echo "list:"
cat $1

echo "list after awk:"
echo `awk -F, '{ print $1 }' $1`

exit 0

我得到这个输出:

$ ./script list
list:
1
2
3
4
list after awk:
1 2 3 4

bash 脚本中的awk神秘地去掉了回车符。

我在 OS X 上的 bash 以及 BSD 上的 zsh 中都看到过这种行为。

有任何想法吗?

答案1

不是的awk,而是 shell 扩展事物的方式。

让我们举个例子:

$ a="1
> 2
> 3
> 4"

所以我们创建了一个超过 4 行的变量。但...

$ echo $a
1 2 3 4

那么为什么它只在一根线上呢?

$ echo "$a"
1
2
3
4

啊,不是的!

所以有一些东西魔法大约echo $a

我们可以在空格中看到一些类似的魔法:

$ a="1    2    3    4"
$ echo $a
1 2 3 4
$ echo "$a"
1    2    3    4

现在,这并不是真正echo发挥魔力的地方。这是外壳。如果没有"..."包装器,shell 将尝试扩展变量。因此,glob 将被扩展:

$ ls
a  file

$ a="*"
$ echo $a
a file
$ echo "$a"
*

以同样的方式,shell 获取包含返回值的输出,扩展它们,然后将其作为参数返回;回报正在丢失。

总之...如果您想防止发生这种扩展,请使用"...".

但在你的简短例子中,你没有需要 echo根本不。直接打电话就可以了awk

答案2

观察这两个命令之间的输出差异:

$ echo `awk -F, '{ print $1 }' $1`
1 2 3 4
$ echo "`awk -F, '{ print $1 }' $1`"
1
2
3
4

在第一个命令中,由于命令替换未加引号,因此 awk 的输出将受到分词路径名扩展分词删除换行符并用空格替换它们。在第二个中,因为awk用双引号引起来,所以没有分词执行并保留换行符。

文档

man bash

分词

shell 扫描未出现在双引号内的参数扩展、命令替换和算术扩展的结果以进行分词。

shell 将 IFS 的每个字符视为分隔符,并使用这些字符作为字段终止符将其他扩展的结果拆分为单词。如果 IFS 未设置,或者其值恰好是 <space><tab><newline>默认值,则忽略先前扩展结果的开头和结尾处的<space><tab>、 和序列,并且不在开头或结尾处的任何 IFS 字符序列用于分隔<newline>字。如果 IFS 具有默认值以外的值,则只要空白字符位于 IFS 值(IFS 空白字符)中,单词开头和结尾的空白字符 space 和 tab 序列就会被忽略。 IFS 中非 IFS 空白的任何字符以及任何相邻的 IFS 空白字符共同界定字段。 IFS 空白字符序列也被视为分隔符。如果IFS的值为空,则不会发生分词。

保留显式空参数(“”或“”)。由于扩展没有值的参数而产生的不带引号的隐式空参数将被删除。如果没有值的参数在双引号内展开,则会产生空参数并被保留。

请注意,如果没有发生扩展,则不会执行拆分。

答案3

不是awk,是echo剥离的。离开echo,只需awk

awk -F, '{ print $1 }' $1

相关内容