将awk脚本应用于文件夹中的多个文件

时间:2013-09-21 23:38:50

标签: bash awk

我想使用以下awk行删除文本文件中的每个偶数行(并保留奇数行)。

awk 'NR%2==1' filename.txt > output

问题是我很难在awk中正确循环或者构建一个shell脚本以将其应用于文件夹中的所有* .txt文件。我试着用这个单线

gawk 'FNR==1{if(o)close(o);o=FILENAME;
sub(/\.txt/,"_oddlines.txt",o)}{NR%2==1; print>o}'  

但是没有删除偶数行。我对shell脚本更不熟悉了。我在gawk下使用win7cygwin使用bash。非常感谢任何想法。

5 个答案:

答案 0 :(得分:3)

你现有的gawk单线非常接近。在这里,它被格式化为更易读的脚本:

FNR == 1 {
    if (o)
        close(o)
    o = FILENAME
    sub(/\.txt/, "_oddlines.txt", o)
}
{
    NR % 2 == 1
    print > o
}

这应该使错误明显 1 。现在我们删除该错误:

FNR == 1 {
    if (o)
        close(o)
    o = FILENAME
    sub(/\.txt/, "_oddlines.txt", o)
}
NR % 2 == 1 {
    print > o
}

$ awk -f foo.awk *.txt

并且它有效(当然你可以重新整理这个)。

(通常情况下,我会像其他答案那样使用for这样做,但我想告诉你你有多接近!)


1 每条评论,也许不是那么明显?

Awk的基本语言构造是“模式 - 动作”语句。 awk 程序只是这些语句的列表。 “模式”是如此命名的,因为它们最初主要是类似grep的正则表达式模式:

$ awk '/^be.*st$/' < /usr/share/dict/web2
beanfeast
beast
[snip]

(除了斜杠,这基本上只是运行grep,因为它使用默认操作print。)

模式实际上可以包含两个地址,但在这些情况下使用一个地址更为典型。未包含在斜杠中的模式允许执行FNR == 1F ile特定NR ecord等于1)或NR % 2 == 1的测试(所有文件中N ecord-cumulative的R数字!-mod 2等于1)。

然而,一旦你击中了开放式支撑,你就会进入“动作”部分。现在NR % 2 == 1只计算结果(true或false),然后将其抛弃。如果完全省略“pattern”部分,则“action”部分将在每个输入行上运行。所以这会打印每一行。

请注意,测试NR % 2 == 1正在测试累积记录号。因此,如果某个文件具有奇数行(“记录”),则下一个文件将打印出每个偶数行(并且这将持续到您使用奇数行搜索另一个文件)。

例如,假设两个输入文件是A.txtB.txt。 Awk开始阅读A.txt并且第一行的FNRNR都设置为1,例如file A, line 1。自FNR == 1第一个“操作”完成后,设置o。然后awk测试第二个模式。 NR为1,因此NR % 2为1,因此第二个“操作”完成,将该行打印到A_oddlines.txt

现在假设文件A.txt只包含那一行。 Awk现在继续提交B.txt,重置FNR但累计NRB的第一行可能是file B, line 1。 Awk尝试第一个“模式”,实际上是FNR == 1,因此关闭旧的o并设置新的模式。

NR2,因为NR所有输入文件中累积。因此,第二个模式(NR % 2 == 1)计算2 % 20)并比较== 1,这是假的,因此awk跳过第1行的第二个“动作”档案B.txt。第2行(如果存在)将包含FNR == 2NR == 3,因此该行将被复制出来。

(我最初假设,因为你的脚本已经接近工作了,你打算这样做,而且只是在语法上留了一点。)

答案 1 :(得分:3)

使用GNU awk,您可以这样做:

$ awk 'FNR%2{print > (FILENAME".odd")}' *.txt

这将为当前目录中仅包含奇数行的每个.odd文件创建一个.txt文件。


然而sed在这里简洁明了。以下GNU sed命令将删除所有偶数行,并为当前目录中的所有.bck文件存储扩展名为.txt的旧文件:

$ sed -ni.bck '1~2p' *txt

<强>演示:

$ ls
f1.txt  f2.txt

$ cat f1.txt
1
2
3
4
5

$ cat f2.txt
6
7
8
9
10

$ sed -ni.bck '1~2p' *txt

$ ls
f1.txt  f1.txt.bck  f2.txt  f2.txt.bck

$ cat f1.txt
1
3
5

$ cat f1.txt.bck
1
2
3
4
5

$ cat f2.txt
6
8
10

$ cat f2.txt.bck
6
7
8
9
10

如果你不支持备份文件,那么只需:

$ sed -ni '1~2p' *txt

答案 2 :(得分:1)

您可以尝试for循环:

#!/bin/bash

for file in dir/*.txt
do    
   oddfile=$(echo "$file" | sed -e 's|\.txt|_odd\.txt|g')  #This will create file_odd.txt
   awk 'NR%2==1' "$file" > "$oddfile"  # This will output it in the same dir.
done

答案 3 :(得分:1)

就个人而言,我会使用

for filename in *.txt; do
    awk 'NR%2==1' "$filename" > "oddlines-$filename"
done

编辑:引用文件名

答案 4 :(得分:1)

您的问题是NR%2==1位于{NR%2==1; print>o}'操作区'内,并没有作为'条件'开始。请改用:

gawk 'FNR==1{if(o)close(o);o=FILENAME;sub(/\.txt/,"_oddlines.txt",o)};
     FNR%2==1{print > o}' *.txt