2014-08-29 39 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