Shell 如何在带有两个循环变量的`for`循环中使用awk

Shell 如何在带有两个循环变量的`for`循环中使用awk,shell,for-loop,awk,Shell,For Loop,Awk,我有一个zsh shell脚本问题:-( 有一个包含4列列表的文件: NAME SURNAME OLD TOWN DOE John 30 London CALAS Maria 50 Athens ... 我只想对每行的一些“元素”进行处理。例如,我不知道这是否可行,但应该是这样的: for user,livesIn in `cat MyFile | awk '{print $2 $4}'`

我有一个zsh shell脚本问题:-( 有一个包含4列列表的文件:

   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
当然,这段代码是错误的,我没有找到如何正确编写它。 有人知道这是否可能吗


提前感谢您的帮助。

awk
一次处理每一行,因此无需for循环。也无需使用
cat
,因为
awk
将文件名作为参数。请尝试以下操作:

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

如果您已经在使用
awk
,为什么不使用
awk
? 例如:

$ awk '{print "My friend", $2, "lives in", $4}' MyFile
    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中进行迭代和处理通常比通过在shell中迭代和在AWK中处理来划分任务更简单、更有效。AWK是为迭代输入而设计的,而且它还具有ts自己的循环结构(例如的)。如果可能,请使用AWK进行所有处理

也就是说,您的问题似乎还需要访问shell中的输入字段,因此在您的情况下,AWK中的完整处理可能不可能(值得注意的是,AWK也可以执行shell命令,但这可能只是另一种复杂程度)

其他答案使用AWK迭代文件并打印第2列和第4列的内容,如

$ awk '{print "My friend", $2, "lives in", $4}' MyFile
如果您可以像这样使用AWK进行迭代和处理,这是很好的。作为这种解决方案的补充,您可能希望跳过第一行(它似乎有列标题,而不是实际数据)

你的评论

事实上,我的治疗比打印要复杂一点。我需要在“for”循环中做一些测试和作业

建议您真正想要的是访问shell中的字段。您可以通过使用AWK挑选字段(如前所述)来实现这一点,但将值管道化到shell中:

    awk 'NR>1{print $2,$4}' MyFile | while read user livesIn; do
        echo "My friend $user lives in $livesIn"
    done
这为您提供了shell中的
$user
$livesIn
,因此您可以使用它进行更复杂的shell处理。例如:

$ awk '{print "My friend", $2, "lives in", $4}' MyFile
    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是在空白处分割的。

当您想循环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

但我意识到,我可能太简化了我的例子……事实上,我的治疗比印刷品要复杂一点。我需要在“for”中做一些测试和作业循环。@Marcoounet你确定吗?我打赌,无论你认为你需要对awk输出做什么,都可以通过awk脚本来完成。谢谢你的回答……你太快了,伙计们:o)“我的朋友DOE住在约翰”?是的,我想你的意思是
$2
$4
,不是
$1
$2
。此外,awk仅通过并排放置字符串来进行字符串连接:
print$2$4
不会用空格分隔字段。在编写shell循环以读取输入时,您希望
print$2、$4
(隐式使用OFS)或显式
print$2”“$4
,请始终包含
IFS=
-r
(例如,
而IFS=read-r用户活动;do
)除非你心中有一个明确的目标,把它们撇在一边,并完全理解其中的含义。另请参见。@EdMorton这是一个很好的观点和链接。单凭表演的成功就可以说服我不要这样做。但在这种情况下,使用空输入字段分隔符
IFS=
将行解释为一个字段,而不是在AWK打印的分隔符(默认空格)上分隔。所以它变成了一个字段,而不是两个,我们失去了
livesIn
值。使用一些模糊的字符(例如
'\v'
,或者只是
'\t'
)并将AWK的
OFS
与shell的
IFS
相匹配是否合适?很好的一点是,在这种情况下,您不能设置
IFS=
,除非您想在将变量作为一行读取后拆分。在这种情况下,由于awk已经从其输出周围去除了所有空白,我想您也可以保留shell IFS,但通常是这样,将shell IFS与awk OFS匹配是有意义的。如果字段很少,则使用虚拟占位符是可以的。但是如果您想使用第20和第25个字段呢?然后你改变主意,真的想要第9和第27个字段。更改一两个数字比计算和添加占位符更容易。