2014-08-29 48 views
0
a=`echo 9.1200 | awk '{ if ($0 ~ /\./){ sub("0*$","",$0); sub ("\\.$","",$0);} print}'` 
$a 

上述awk返回9.1外壳脚本内部。但在unix控制台中执行相同的awk时。
它返回的值9.12
awk中围捕号码

为什么awk围捕号码及如何避免这种情况?

以下是样本输入和出文件值

Sample input: 
10 
10.1 
10.0 
10.00 
10.0000 
10.0000000 
10.58770 
10.580 
10.2555550003 

Expected Output: 
10 
10.1 
10 
10 
10 
10 
10.5877 
10.58 
10.2555550003 
+0

期望的行为是什么? – 2014-08-29 10:29:47

+0

我想要的输出是9.12 – Marjer 2014-08-29 10:30:18

+0

脚本是否应该删除多余的额外零?你能提供一些不同的输入和相应的期望输出吗? – 2014-08-29 10:30:55

回答

3

awk不是四舍五入的,你用final final()命令去掉最后一位数字。看:

$ a=`echo 9.1200 | awk '{ if ($0 ~ /\./){ sub("0*$","",$0); sub ("\\.$","",$0);} print}' 
$ echo $a 
9.1 

$ a=`echo 9.1200 | awk '{ if ($0 ~ /\./){ sub("0*$","",$0); sub ("[.]$","",$0);} print}'` 
$ echo $a 
9.12 

你的命令行有几个错误。

  1. 子()需要一个RE,因为它的第一个参数,因此概要使用一个明确的RE时是不sub(/RE/,...)sub("RE",...)(谷歌AWK解析字符串文字)。
  2. sub()的第三个参数默认为$ 0,所以不需要明确加上 它。
  3. 你不需要多次调用sub()去除 字符串的末尾,只是一个简单的ERE。
  4. awk脚本为<condition> { <action> }块,所以不要将 条件置于操作块内。
  5. 将字符串转换为数字的自然方法就是使用 数字运算符,而不是尝试操纵字符串以像数字一样查找 。
  6. 不要使用弃用的反引号来调用命令,因为除其他外,他们解释反斜杠,所以awk命令看到的是sub("\.$"..而不是您想要的sub("\\.$"..
  7. 总是给shell变量

这里有一个有效的语法做你尝试使用字符串操作做什么:

$ a=$(echo 9.1200 | awk '/\./{ gsub(/\.0*$/,""); print}') 
$ echo "$a" 
9.12 

但这是简单的:

$ a=$(echo 9.1200 | awk '/\./{ print $0+0 }') 
$ echo "$a" 
9.12 

和如果输入数据始终为数字,则根本不需要测试/\./

$ a=$(echo 9.1200 | awk '{print $0+0}') 
$ echo "$a" 
9.12 

你遇到的主要问题是使用反斜杠,所以让我稍微解决一下。当你写的包括RE元字符,如.你想当作文字字符的RE你有2种选择:

/\./ 

/[.]/ 

让我们假设你决定使用前者。一切都很好,直到你决定使用字符串分隔符而不是RE分隔符。字符串文字解释了两次,一次是当脚本再次读取并执行时会这样逃跑元字符,你需要转义两次的RE,例如:

"\\." 

现在让我们假设你决定调用你的awk脚本将输出保存在shell变量中。你有两个选择:

var=`awk '...'` 

或:

var=$(awk '...') 

当您使用后者是没有问题的,但是当你使用前者,那`...`本身语法解释对反斜线的单反斜线,所以你需要添加另一个反斜杠逃脱.,即:

var=`awk '... "\\\." ...'` 

显然转义失控。

所以 - 为了避免反斜线地狱,使用的RE时使用RE定界符/.../尽可能*而不是字符串分隔符"...",当执行shell脚本使用$(...),而不是旧风格的`...`。 *当您需要字符串分隔符时,您需要将文字RE段与变量连接起来或在变量中保存一个RE,例如, var="a.b"; sub(var,"")sub(var".*","")

+1

OP的原始代码与'$()'一起工作似乎确实很微妙。我知道它们已被弃用,但它为什么会改变awk命令的行为令人困惑。 – 2014-08-29 13:24:09

+2

使用'\'... \''和'$(...)'之间的区别之一是,当使用'\'... \''时,2个反斜杠被解释为1,但是当使用'$(...)',将2个反斜杠视为字面上的2个反斜杠。因此,在调用awk时,sub()在第一种情况下会看到'“\。”',但在第二种情况下会看到“\\。”'。 – 2014-08-29 13:39:38

+2

+1以获取原始问题的根源。 – 2014-08-29 13:54:33

1

如果你只是想删除前导和尾随零,这将做到:

echo 09.1200 | awk '{ print +$0 }' 

或者像有些人更喜欢(更广泛地兼容):

echo 09.1200 | awk '{ print $0+0 }' 

这两个导致awk将输入转换为数字。 (注意:the first one will not work on all versions of awk在这两种情况下

输出(使用GAWK):

9.12 

对于更高的精度,可以使用这样的事情:

awk '{ printf "%.12g\n", $0 }' file 

12指定的最大数量小数位数

作为@Jotne has suggested,您应该使用a=$(awk '{ printf "%.12g\n", $0 }' file)而不是使用反引号。结果是你的原代码工作,以及我建议的方法。

+0

'+ $ 0'这是新的。感谢您给我更多'awk'提示和技巧。 – Jotne 2014-08-29 10:37:42

+0

'echo 9.1200 | awk'{if($ 0〜/\./){sub(“0 * $”,“”,$ 0); sub(“\\。$”,“”,$ 0);} printf“%.12g \ n”,$ 0}''输出仍然是9。1在shell脚本中 – Marjer 2014-08-29 10:45:53

+0

@Marjer你试过我的方法吗?最后一个是最灵活的,但你可能能够逃脱使用第一个(相当于)之一。 – 2014-08-29 10:49:28