2013-01-24 36 views
13

有沒有辦法在awk以內的當前shell中設置變量?在awk的當前shell中設置變量

我想對一個文件進行一些處理,並打印出一些數據;因爲我會讀整個文件,所以我想節省行數 - 在這種情況下,FNR

偏偏雖然我似乎無法找到一種方法來設置與FNR值shell變量;如果不是這樣的話,我必須從我的輸出文件中讀取FNR,以設置num_lines,其值爲FNR

我一直在使用awk 'END{system(...)}'嘗試了一些組合,但不能管理它的工作。任何方式在這個?

回答

16
$ echo "$var" 

$ declare $(awk 'BEGIN{print "var=17"}') 
$ echo "$var" 
17 

這也是爲什麼你應該使用聲明的,而不是EVAL:

$ eval $(awk 'BEGIN{print "echo \"removing all of your files, ha ha ha....\""}') 
removing all of your files, ha ha ha.... 

$ declare $(awk 'BEGIN{print "echo \"removing all of your files\""}') 
bash: declare: `"removing': not a valid identifier 
bash: declare: `files"': not a valid identifier 

注意在EVAL執行的任何字符串AWK打印第一種情況下,這可能會意外地是一個非常糟糕的事情!

+0

+1,但只要是我編寫代碼,使用'eval'我就沒有多大的問題了。 – Rubens

+10

@Rubens,直到你意外地做壞事的那一天。如果您現在習慣於編寫安全代碼,您將來不會被咬。 –

+0

@glennjackman +1感謝您的提示;我想這就是爲什麼在這裏考慮安全編碼的原因(: – Rubens

1

awk打印出來的賦值語句:

MYVAR=NewValue 

然後在你的shell腳本,evalawk腳本的輸出:

eval $(awk ....) 
# then use $MYVAR 

編輯:人建議使用declare代替eval,如果內部腳本打印的是分配以外的內容,則稍微不太容易出錯。它只是bash而已,但是當shell bash且腳本具有#!/bin/bash時,可以正確指出此依賴關係。

eval $(...)變體被廣泛使用,現有的程序產生適合eval的輸出,但不適用於declarelesspipe是一個例子);這就是爲什麼理解它很重要的原因,而且僅限bash的變體是「太本地化」的。

+0

這是唯一在技術上可能的答案 – anishsane

+0

@anishsane似乎是合理的,我(:!也想不到這一點,雖然謝謝,安東·科瓦連科 – Rubens

+0

我會用'declare',而不是'eval' – chepner

4

您無法將變量從子shell導出到其父shell。你有一些其他的選擇,不過,包括:

  1. 請使用AWK來計算記錄文件的另一個通,並用命令替換來捕獲結果。例如:

    FNR=$(awk 'END {print FNR}' filename) 
    
  2. 打印FNR在子shell,並分析你的其他過程的輸出。
  3. 如果FNR相同的行數,你可以調用wc -l < filename,讓您的計數。
+0

第二和第三選項是我想要避免的,但是我沒有真正從第一個選項中獲得這個想法。是不是我在第三個中使用的第一個完全相同的技術? – Rubens

+0

Yep,'FNR = $(awk'END {print FNR}'filename)'和'FNR = $(wc -l filename | awk'{print $ 1}')'是完全相同的,除了一個程序計數行 - 'awk' /'wc'。 – Rubens

+3

你不會真的那樣做wc + awk組合,不過,你只需要用'wc -l <​​filename'來代替。 –

14

這是另一種方法。

時,當你有值的變量的變量,你想拆起來這是非常有用的。例如,您可以從數據庫中的單行中獲取要創建變量的值列表。

val="hello|beautiful|world" # assume this string comes from a database query 
read a b c <<< $(echo ${val} | awk -F"|" '{print $1" "$2" "$3}') 

echo $a #hello 
echo $b #beautiful 
echo $C#world 

我們需要「這裏串」在這種情況下,即< < <,因爲讀命令不會從管道中讀取並從標準

+2

這正是我所需要的!我已經有了一個由空格分隔的字符串,所以我只是在輸入就像在「read abc <<< $ var」中一樣。謝謝 –

+0

<3 ..感謝。 – calbertts

+0

有一點需要提一下,如果字段之間用空格隔開,可以簡化爲: ''' read abc < << $(echo $ {val}) ''' –

3

警告任何人試圖使用聲明爲而不是讀由幾個答案建議。

eval沒有這個問題。

如果提供的awk(或其他表達式)聲明結果爲空字符串,則declare將轉儲當前環境。 這幾乎肯定不是你想要的。

例如:如果您的awk模式不存在於輸入中,您將永遠不會打印輸出,因此最終會出現意外的行爲。

這樣的一個例子....

unset var 
var=99 
declare $(echo "foobar" | awk '/fail/ {print "var=17"}') 
echo "var=$var" 
var=99 
The current environment as seen by declare is printed 
and $var is not changed 

一個小的改動,以存儲該值在AWK變量來設置並在結束打印解決了這個....

unset var 
var=99 
declare $(echo "foobar" | awk '/fail/ {tmp="17"} END {print "var="tmp}') 
echo "var=$var" 
var= 
This time $var is unset ie: set to the null string var='' 
and there is no unwanted output. 

要顯示有匹配模式

unset var 
var=99 
declare $(echo "foobar" | awk '/foo/ {tmp="17"} END {print "var="tmp}') 
echo "var=$var" 
var= 
This time $var is unset ie: set to the null string var='' 
and there is no unwanted output. 
1

要合成這裏的一切這方面的工作,到目前爲止,我會分享我覺得是非常有用的從使用awk讀取單行文件的腳本設置shell環境變量。顯然/pattern/可以用來代替NR==1找到需要的變量。

# export a variable from a script (such as in a .dotfile) 
declare $(awk 'NR==1 {tmp=$1} END {print "SHELL_VAR=" tmp}' /path/to/file) 
export SHELL_VAR 

這將避免變量的大量輸出,如果declare命令不帶參數,還有的盲eval的安全風險發出。