2016-02-01 195 views
1

我有一個的zsh shell腳本問題:-( 有包含4列的列表中的文件:如何在`for`循環使用AWK兩個循環變量

NAME SURNAME  OLD  TOWN 
    DOE  John  30  London 
    CALAS Maria  50  Athens 
    ... 

我要打處理每行只有一些「元素」。例如,我不知道這是可能的,但它應該是這樣的:

for user,livesIn in `cat MyFile | awk '{print $2 $4}'` 
    echo "My friend $user lives in $livesIn" 
done 

當然這個代碼是錯誤的,我沒有找到如何寫得對。 有人知道這是可能嗎?

在此先感謝您的幫助。

+0

此外,awk通過並排放置字符串來實現字符串連接:'print $ 2 $ 4'不會用空格分隔字段。你想要'打印$ 2,$ 4'(它隱式使用OFS)或明確地'打印$ 2''$ 4' –

回答

0

它通常是更簡單,更有效的做到既迭代處理在AWK,而不是試圖通過在AWK的外殼和處理迭代劃分任務。 AWK很適合迭代輸入,並且它也有自己的循環結構(例如for)。如果可能,請在AWK中進行所有處理。

這就是說,你的問題似乎也需要訪問shell中的輸入字段,所以在你的情況下AWK中的完整處理可能是不可能的。 (值得一提的是,AWK也可以執行shell命令,但是這可能是複雜的只是一個級別。)

其他答案用AWK通過文件和打印的東西2列4迭代,像

$ awk '{print "My friend", $2, "lives in", $4}' MyFile 

這很好,如果你可以迭代和處理這樣的AWK。作爲除了這種類型的解決方案,你可能想跳過第一行(這似乎有列標題,而不是實際的數據)

$ awk 'NR>1{print "My friend", $2, "lives in", $4}' MyFile 

您的評論

其實我治療比印刷更復雜一點。我需要在「for」循環中進行一些測試和分配。

表明你真正想要的是訪問你的shell中的字段。您可以通過使用AWK挑出來的領域(如前),但配管的值入殼得到這樣的:

awk 'NR>1{print $2,$4}' MyFile | while read user livesIn; do 
     echo "My friend $user lives in $livesIn" 
    done 

這讓你在shell $user$livesIn,所以你可以做更復雜的殼處理它。例如:

awk 'NR>1{print $2,$4}' MyFile | while read user livesIn; do 
     if [[ "$user" == "John" ]]; then 
      echo "$user is not my friend, but lives in $livesIn" 
     else 
      echo "My friend $user lives in $livesIn" 
      echo "$user" >> friends.txt 
     fi 
    done 

由於AWK在空白區域分裂,所以要小心輸入文件的格式。

+1

在編寫shell循環來讀取輸入時,除非你有一個特定的目的,把它們全部刪除並且完全理解,否則總是包含'IFS ='和'-r'(例如'IFS = read -r user livesIn; do')所有的影響。另請參閱http://unix.stackexchange.com/q/169716/133219。 –

+1

@EdMorton這是一個很好的點和一個很好的鏈接。單憑性能會讓我相信不要這樣做。但在這種情況下,使用空輸入字段分隔符「IFS ='將行解釋爲一個字段,而不是在由AWK打印的分隔符(默認空間)上分隔。所以它變成了一個領域,而不是兩個,我們失去了「生命」的價值。使用一些模糊的字符(比如''\ v'',或者''\ t'')並且將AWK的'OFS'和shell的'IFS'匹配是否合適? – e0k

+0

好的一點是,在這種情況下,你不能設置IFS =',除非你想在將它讀作單行之後將變量分開。在這種情況下,因爲awk已經從輸出周圍剝離了所有的空白空間,所以我猜你可能會離開shell IFS,但通常情況下是這樣,將shell IFS與awk OFS相匹配是有道理的。 –

1

awk每次處理一行,因此不需要for循環。也不需要cat作爲awk將文件名稱作爲參數。試試這個:

awk '{print "My friend "$2" lives in "$4}' MyFile 
+0

感謝您的回答......您太快了:o) – Marcoounet

+0

「我的朋友DOE住在約翰「? –

+0

是的,我想你的意思是$ 2和$ 4,而不是$ 1和$ 2。 – e0k

1

如果您已經使用awk,你爲什麼不處理與awk一切嗎? 例如:

$ awk '{print "My friend", $2, "lives in", $4}' MyFile 

這會得到您正在查找的輸出。除非問題中沒有其他內容。

+0

嗨,非常感謝您的及時答覆。 – Marcoounet

+0

但我意識到,我很可能簡化了我的例子......事實上,我的治療比印刷更復雜一點。我需要在「for」循環中進行一些測試和分配。 – Marcoounet

+0

@Marcoounet你確定嗎?我敢打賭,無論你認爲你需要用awk輸出做什麼,最好在awk腳本中完成。 –

1

當你想通過AWK的輸出循環,你可以嘗試

awk '{print $2 $4}' MyFile | while read -r user livesIn; do 
    echo "${user} was last seen in ${livesIn}." 
done 

在這種情況下,不需要awk:當某些領域有着

while read -r field1 user field3 livesIn; do 
    echo "${user} was last seen in ${livesIn}." 
done < MyFile 

以上結構將失敗空間,如New York
仔細查看MyFile的規格,字段是如何分離的。固定寬度? TAB字符?使用TAB,你是幸運的:

while IFS=$'\t' read -r field1 user field3 livesIn; do 
    echo "${user} was last seen in ${livesIn}." 
done < MyFile 
+1

如果字段很少,則使用虛擬佔位符是很好的。但是如果你想使用第20和第25個字段呢?然後你改變主意,真正想要第9和第27場。更改一個或兩個數字比計算和添加佔位符更容易。 – e0k