如何删除目录中所有文件的相同行号?

如何删除目录中所有文件的相同行号?

我想从目录中的每个 .fasta 文件中删除第 151-154 行。我在尝试

find . -type f -exec sed -i.fix '151,154d' '{}' '+' 

但它只会在第一个文件上运行,而不会在其他 400 个文件上运行。

答案1

-i是一个非标准sed选项。它来自perl。 GNU 和 FreeBSD在 2001 年底、2002 年初独立sed添加,以模仿的行为,但具有不同的界面。由于有更多的变化(特别是关于保留了多少原始文件元数据),它已经进入了更多的实现中。-iperl

在 中perl,你会这样做:

perl -ni.back -e 'print unless $. == 151..154' file

$.是当前行号当前输入文件句柄。当使用<>/时,文件句柄会依次为作为参数传递的每个文件打开,但由于在每个文件之间没有关闭,因此不会在每个文件之间重置。为此,您需要:-nARGVARGV$.

perl -ni.back -e '
  print unless $. == 151..154;
  close ARGV if eof' file1 file2

GNUsed是第一个添加-i选项 à la的实现 (AFAIK) perl(于 2001-09-25 添加,但直到一年后才发布(在 3.95 中)),在每个文件之间重置了行号(-s暗示另一个 GNU 扩展) 。

自由BSD做了不同的事情。 GNU 和 FreeBSD 的原始 API 之间的主要区别在于,-i在 FreeBSD 中需要一个参数,而在 GNU 中它是可选的,sedperl.在 FreeBSD 中,最初,每个文件之间的行号并未重置。

2007年,FreeBSDsed在第二点上与 GNU 保持一致。每个文件之间的行号已重置,并且-I添加了一个选项以获取-i.

-i很久以后,其他一些sed实现(如 busybox、NetBSD 和 OpenBSD)才添加了支持,但它们sed在这两点上都与 GNU 保持一致。

macOSsed基于旧版本的 FreeBSD,因此可能是当今唯一按照旧版本 FreeBSD 的方式运行的实现,也可能是您正在使用的实现。

因此,在这里,您需要使用perl

find . -type f ! -name '*.back' -exec perl -ni.back -ne '
  print unless $. == 151..154;
  close ARGV if eof' {} +

或者为sed每个文件调用一个:

find . -type f ! -name '*.back' -exec sed -i.back 151,154d {} \;

或者安装并使用 GNUsed或 GNU awk(使用-i /usr/share/awk/inplace.awk1 并匹配FNR,而不是NR)或ed/ex基于方法


^不使用-i inplaceas尝试首先从当前工作目录gawk加载inplace扩展(asinplace或),有人可能已经在其中植入了恶意软件。随系统提供的扩展inplace.awk的路径可能会有所不同,请参阅输出inplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

答案2

这个答案使用ex而不是sed -i因为sed -i不是真正的就地编辑。它写入临时文件,然后重命名它以替换原始文件,这会导致索引节点号发生变化。这会破坏 COW 文件系统(如 btrfs 或 zfs)上的任何硬链接和快照以及依赖于文件 inode 编号的任何其他内容。大多数其他实现-i/--in-place选项(包括perl's-i选项)的常见命令也是如此。

find . -type f -exec sh -c 'for f in "$@" ; do
    cp -a "$f" "$f.fix"
    printf "%s\n" 151,154d x | ex -s -- "$f" 
  done' sh {} +

此分支sh用于处理 . 找到的文件列表find

sh过程首先创建原始文件的备份副本(使用 GNUcp-a选项来保留时间戳和权限等所有属性),然后调用ex 编辑该文件。

上面的管道printf ... | ex ...两个ex命令(*)into ex,每个命令用换行符分隔。第一个命令删除第 151-154 行。第二个 ( x) 指示ex将更改写入文件(默认情况下,ex将退出而不保存,除非您告诉它保存)。如果没有进行任何更改,则退出而不保存,与中的命令x相同。:xvi

注意:如果任何单个输入文件少于 154 行,则将对该文件进行更改。

-s选项ex告诉它保持沉默,并且不打印任何诊断消息。

防止--任何文件名参数被解释为选项ex(如果任何文件名以 开头-)。这在这里并不是绝对必要的,因为find这里命令输出的所有文件名都会有./前缀 - 但这是无害的,并且在处理未知文件名时是一个好习惯。

(*) ex命令与命令几乎相同sed。原来,很久以前,ed是文件编辑器,sed是基于ed.后来,vi被编写为可视化版本edvi添加了可视化编辑模式,但保留了命令模式ed下的命令:vi也可以像命令模式编辑一样运行,ex而无需视觉模式,这就是此处使用的模式。 ex仍然主要是克隆,但自首次编写以来的几十年里比原始版本ed获得了一些小的改进(如命令)。xed

答案3

你走在正确的轨道上。只需要稍微编辑一下你的命令。您可能想使用 xargs (而不是 exec)。这是一个应该可以工作的版本:

$ find . -type f | xargs -I{} sed -i '151,154d' {}

相关内容