![我想从许多文件中 grep 出一个文本字符串,如何为此运行循环](https://linux22.com/image/84310/%E6%88%91%E6%83%B3%E4%BB%8E%E8%AE%B8%E5%A4%9A%E6%96%87%E4%BB%B6%E4%B8%AD%20grep%20%E5%87%BA%E4%B8%80%E4%B8%AA%E6%96%87%E6%9C%AC%E5%AD%97%E7%AC%A6%E4%B8%B2%EF%BC%8C%E5%A6%82%E4%BD%95%E4%B8%BA%E6%AD%A4%E8%BF%90%E8%A1%8C%E5%BE%AA%E7%8E%AF.png)
我有一千个.xyz
文件,所有数字数据都在同一目录中。其中一些的末尾有文本字符串“END”。我不确定如何循环grep
命令,以便删除“END”并创建一个新文件,同时在原始文件名的末尾添加一些内容。
我的一些文件的最后几行示例
-1.10949170 55.68890280 -67.60000000
-0.92807500 55.64095280 -89.80000000
-0.95770560 55.66495830 -82.40000000
-0.90995000 55.63676110 -94.00000000
-1.03738890 55.65107220 -70.20000000
END
我想要从许多文件中删除 END
-1.10949170 55.68890280 -67.60000000
-0.92807500 55.64095280 -89.80000000
-0.95770560 55.66495830 -82.40000000
-0.90995000 55.63676110 -94.00000000
-1.03738890 55.65107220 -70.20000000
原文件名:survey_2015_xxx.xyz
新文件名: survey_2015_xxx_s.xyz
答案1
如果您head
支持负偏移量:
for file in *.xyz; do
if [ "$(tail -n 1 < "$file")" = END ]; then
head -n -1 < "$file" > "${file%.xyz}_s.xyz"
fi
done
(如果没有,则替换head -n -1
为sed '$d'
)。
您可以通过以下方式提高效率ksh93
:
for file in *.xyz; do
if IFS= read -r last4 < "$file" <#((EOF-4)) <#((here=CUR)) &&
[ "$last4" = END ]; then
command /opt/ast/bin/head -c "$here" < "$file" > "${file/%.xyz/_s\0}"
fi
done
因为它仅使用内置命令。
如果您的文件系统支持引用链接副本(即复制文件,其中数据在修改之前不会重复,这将节省时间和磁盘空间),您仍然可以使用ksh93
GNU cp
:
for file in *.xyz; do
if IFS= read -r last4 < "$file" <#((EOF-4)) &&
[ "$last4" = END ]; then
newfile=${file/%.xyz/_s\0}
cp --reflink=auto -- "$file" "$newfile" &&
: 1<>; "$newfile" >#((EOF-4))
fi
done
也就是说,我们正在制作一个 reflink 副本并从末尾截断 4 个字节。
答案2
另一种ed
选择,因为你只活一次:
for f in *.xyz; do
printf '%s\n' ';g/END/d\' "w ${f%.xyz}_s.xyz" q | ed -s "$f"
done
文件名注意事项:
- 文件名不能以 开头
!
,否则ed
将尝试将缓冲区通过管道传输到命令中。 - 由于
ed
使用空白字符将w
命令与其路径名参数分隔开,因此文件名中的前导空格将不会被保留。 - 带有换行符的文件名会导致问题,因为
ed
使用换行符来分隔命令。
在一个理智的世界中,这些限制都无关紧要。唉……
答案3
只是使用其他解决方案sed
for file in *.xyz
do
if [ "$(sed -e '$!d' $file)" = END ]
then
sed -e '$d' $file > $file_s.xyz
fi
done