sed 删除许多文件中除第一行和最后一行之外的所有内容

sed 删除许多文件中除第一行和最后一行之外的所有内容

我想处理一个目录中的许多 *.txt 文件 - 它们具有通用结构(;分隔的通用标头)每条线的行数各不相同,有些只是单行,有些则长达 8 行。

我想删除每个文件除第一行和最后一行之外的任何行。有什么有用的指点吗?

更新:我已按要求提供了一些测试数据文件:

stat87.txt

Stations_id; Stationshoehe; Geogr.Breite; Geogr.Laenge; von_datum; bis_datum; Stationsname;                                                                                                             
 87;     ; 46.1123;  8.5440;19010101;19661229;Dres

stat01.txt

Stations_id; Stationshoehe; Geogr.Breite; Geogr.Laenge; von_datum; bis_datum; Stationsname;                                                                                                             
 1;     ; 47.8400;  8.8500;18910101;19580228;Aach                                                                                                                                                   
 1;  478; 47.8413;  8.8493;19580301;19860630;Aach

例如 stat56.txt。

Stations_id; Stationshoehe; Geogr.Breite; Geogr.Laenge; von_datum; bis_datum; Stationsname;                                                                                                             
 56;     ; 46.4580;  7.6320;18980101;19450321;Hamb
 56;     ; 46.4580;  7.6320;19450321;19880511;Hamb                                                                                                                                                   
 56;  103; 46.4411;  7.6345;19880601;19990630;Hamb

在这种情况下,我会特别希望保留第 5 列的第一行和第 6 列的最后一行,以便捕获车站的时间跨度。

结果:

find . -type f -name \*.txt -printf "%f\0" | xargs -0 -I xxxx sed -ni '
2 {
  $ {
    s/^[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\);\([^;]*\).*$/\1;\2/
    p
    q
  }
  s/^[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\).*$/\1/
  p
}
$ {
  s/^[^;]*;[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\).*$/\1/
  p
}' xxxx

产生....

19010101;19661229

18910101
19860630

18980101
19990630

然后,我使用一个简单的 sed 循环通过添加 ; 来清理最终文件。

'for file in *.txt; do
  sed 'N;s/\n/;/' "$file" > "cleaned$file"
 done'

19010101;19661229

18910101;19860630

18980101;19990630

答案1

完美,下面有 awk 版本:

find . -type f -name \*.txt -printf "%f\0" | xargs -0 -I xxxx sed -ni '
 2 {
   $ {
     s/^[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\);\([^;]*\).*$/\1;\2/
     p
     q
   }
   s/^[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\).*$/\1/
   h
 }
 $ {
   s/^[^;]*;[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\).*$/\1/
   H
   x
   s/\n/;/
   p
 }' xxxx

谢谢非常有名的人Sed - Bruce Barnett 的介绍和教程

结果:

$ cat stat01.txt
18910101;19860630
$ cat stat56.txt
18980101;19990630
$ cat stat87.txt
19010101;19661229

----


第一个版本供参考

根据您的输入,我发明了数据文件格式和 sed 脚本来处理它们。

尝试一下:

$ find . -type f -name \*.txt -printf "%f\0" | xargs -0 -I xxxx sed -ni '
 2 {
   $ {
     s/^[^;]*;\([^;]*\);\([^;]*\).*$/\1;\2/
     p
     q
   }
   s/^[^;]*;\([^;]*\).*$/\1/
   p
 }
 $ {
   s/^[^;]*;[^;]*;\([^;]*\).*$/\1/
   p
 }' xxxx

它删除包含标题的第一行。

它仅保留遇到的第一个数据行的第 2 列和文件的最后一个数据行的第 3 列。

如果文件只包含一个数据行,则第 2 列和第 3 列保留在一行上。

呵呵,这很奇怪,但我玩得很开心!

当前目录下的数据文件:

$ cat test01.txt
Name;Price;Amount;Description
Bread;2.1;3;healthy one
$ cat test02.txt
Name;Price;Amount;Description
Water;0.0;100;For life
Wine;10.3;1;Less than half a glass a day
$ cat test03.txt
Name;Price;Amount;Description
House;1000.0;1;home
Car;500.5;0;no need
Bike;10.3;5;Good for the planet and for me

结果:

$ cat test01.txt
2.1;3
$ cat test02.txt
0.0
1
$ cat test03.txt
1000.0
5

请提供2个简短的数据文件内容和预期结果,我会修改这个答案。

答案2

为此,您需要对文件进行循环:

for file in *.txt; do
  lines=$(wc -l < "$file")
  if [ "$lines" -lt 3 ]; then
    echo "$file is short enough, not touching it."
  else
    # for testing, you can also use the -i option
    sed -n '1p;$p' "$file" > "$file.new"
  fi
done

如果您的文件只有一行长,则循环是必要的。随着thrig 给出的命令他们会出现两次(尝试echo 1|sed -n '1p;$p')。

答案3

对于这项任务来说,Gawk 是比 sed 更好的工具。重新利用原始方法的 find-xargs 管道并使用相同的输出命名法:

find . -type f -name \*.txt -printf "%f\0" | xargs -0 gawk -F\; '
    FNR==2  { von = $5 }
    ENDFILE { print von FS $6 > "cleaned" FILENAME }
'

代码变得更简单、更清晰并且更易于维护。

相关内容