我想从许多文件中 grep 出一个文本字符串,如何为此运行循环

我想从许多文件中 grep 出一个文本字符串,如何为此运行循环

我有一千个.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 -1sed '$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

因为它仅使用内置命令。

如果您的文件系统支持引用链接副本(即复制文件,其中数据在修改之前不会重复,这将节省时间和磁盘空间),您仍然可以使用ksh93GNU 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

相关内容