![sed 删除许多文件中除第一行和最后一行之外的所有内容](https://linux22.com/image/86592/sed%20%E5%88%A0%E9%99%A4%E8%AE%B8%E5%A4%9A%E6%96%87%E4%BB%B6%E4%B8%AD%E9%99%A4%E7%AC%AC%E4%B8%80%E8%A1%8C%E5%92%8C%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E4%B9%8B%E5%A4%96%E7%9A%84%E6%89%80%E6%9C%89%E5%86%85%E5%AE%B9.png)
我想处理一个目录中的许多 *.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 }
'
代码变得更简单、更清晰并且更易于维护。