我想从目录中的每个 .fasta 文件中删除第 151-154 行。我在尝试
find . -type f -exec sed -i.fix '151,154d' '{}' '+'
但它只会在第一个文件上运行,而不会在其他 400 个文件上运行。
答案1
-i
是一个非标准sed
选项。它来自perl
。 GNU 和 FreeBSD在 2001 年底、2002 年初独立sed
添加,以模仿的行为,但具有不同的界面。由于有更多的变化(特别是关于保留了多少原始文件元数据),它已经进入了更多的实现中。-i
perl
在 中perl
,你会这样做:
perl -ni.back -e 'print unless $. == 151..154' file
$.
是当前行号当前输入文件句柄。当使用<>
/时,文件句柄会依次为作为参数传递的每个文件打开,但由于在每个文件之间没有关闭,因此不会在每个文件之间重置。为此,您需要:-n
ARGV
ARGV
$.
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 中它是可选的,sed
如perl
.在 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.awk
1 并匹配FNR
,而不是NR
)或ed
/ex
基于方法。
^不使用-i inplace
as尝试首先从当前工作目录gawk
加载inplace
扩展(asinplace
或),有人可能已经在其中植入了恶意软件。随系统提供的扩展inplace.awk
的路径可能会有所不同,请参阅输出inplace
gawk
gawk '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
相同。:x
vi
注意:如果任何单个输入文件少于 154 行,则不将对该文件进行更改。
该-s
选项ex
告诉它保持沉默,并且不打印任何诊断消息。
防止--
任何文件名参数被解释为选项ex
(如果任何文件名以 开头-
)。这在这里并不是绝对必要的,因为find
这里命令输出的所有文件名都会有./
前缀 - 但这是无害的,并且在处理未知文件名时是一个好习惯。
(*) ex
命令与命令几乎相同sed
。原来,很久以前,ed
是文件编辑器,sed
是基于ed
.后来,vi
被编写为可视化版本ed
。 vi
添加了可视化编辑模式,但保留了命令模式ed
下的命令:
。 vi
也可以像命令模式编辑一样运行,ex
而无需视觉模式,这就是此处使用的模式。 ex
仍然主要是克隆,但自首次编写以来的几十年里比原始版本ed
获得了一些小的改进(如命令)。x
ed
答案3
你走在正确的轨道上。只需要稍微编辑一下你的命令。您可能想使用 xargs (而不是 exec)。这是一个应该可以工作的版本:
$ find . -type f | xargs -I{} sed -i '151,154d' {}