我试图在 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