【问题标题】:Removing lines from multiple files with sed command使用 sed 命令从多个文件中删除行
【发布时间】:2015-08-07 10:49:47
【问题描述】:

所以,免责声明:我对使用 bash 和 zsh 很陌生,所以答案很简单。尽管如此。我检查了以前的帖子,找不到任何东西。 (编辑:我在 bash 和 zsh shells 中都试过这个——同样的问题。)

我有一个包含许多文件的目录,并试图从每个文件中删除第一行。

所以说目录包含:file1.txt file2.txt file3.txt ...等

我正在使用 sed 命令(非 GNU):

sed -i -e "1d" *.txt

出于某种原因,这只是删除了第一个文件的第一行。我认为 *.txt 会影响目录中与模式匹配的所有文件。奇怪的是,它正在创建附加了 -e 的文件副本,但副本和原始文件都是相同的。

我用其他命令(例如 ls *.txt)尝试了这个,它工作正常。我缺少关于 sed 的内容吗?

提前谢谢你。

【问题讨论】:

  • 你使用的是 bash 还是 zsh?
  • 我的印象是几乎没有区别(至少对于我的初学者来说)。目前我正在使用 zsh。

标签: macos bash shell sed zsh


【解决方案1】:

不同操作系统中sed的不同版本支持不同的参数。

OpenBSD (5.4) sed

-i 标志不可用。您可以使用以下/bin/sh 语法:

for i in *.txt
do
    f=`mktemp -p .` 
    sed -e "1d" "${i}" > "${f}" && mv -- "${f}" "${i}"
done

FreeBSD (11-CURRENT) sed

-i 标志需要扩展,即使它是空的。因此必须写成sed -i "" -e "1d" *.txt

GNU sed

这会查看-i 后面的参数是否是另一个选项(或者可能是一个命令)。如果是这样,它假定就地修改。如果它看起来是“.bak”之类的文件扩展名,它将用“.bak”重命名原始文件,然后将其修改为原始文件的名称。

其他平台上可能还有其他变体,但我手头只有这三个。

【讨论】:

  • 我将在文本中添加 POSIX 版本(即OpenBSD 版本)。并尝试在可用时在 sed 操作中使用简单的引号(避免任何 shell 解释)。
  • 非常感谢您抽出宝贵时间回复此问题,Gumnos。您对 for 循环的建议非常有效。干杯!
  • 更新到这个答案:OpenBSD 最新版本中的sed 现在支持-i 标志,其方式与 FreeBSD 相同。
【解决方案2】:
  1. 在没有 -e 的情况下使用它!

一个文件使用:

sed -i '1d' filename

所有文件使用:

sed -i '1d' *.txt

files=/path/to/files/*.extension ; for var in $files ; do sed -i '1d' $var ; done

. 对我来说,我使用基于 ubuntu 和 debian 的系统,这种方法对我来说 100% 有效,但对于其他平台我不确定,所以这是另一种方法:

  1. 将第一行替换为 emty pattern ,并删除空行,(双命令):

    对于 $(ls /path/to/files/*.txt) 中的文件;做 sed -i "s/$(head -1 "$files")//g" "$files" ; sed -i '/^$/d' "$files" ;完成

注意:如果你的文件包含 splash '/' ,那么它会报错,所以在这种情况下 sed 命令应该是这样的(sed -i "s[$(head -1 "$files")[[g"

希望这就是你想要的:)

【讨论】:

  • 感谢您的回复。我有 BSD sed,所以我必须为“-i”提供一些扩展名,否则会出错。即使我尝试添加 -i '' 的变通方法也会发生同样的事情。
  • 我不是一个很好的 linuxer,但我会在这个答案中添加更多命令,希望它会有所帮助。
  • 太棒了!非常感谢你,尤尼斯。现在都搞定了。
【解决方案3】:

这里的问题是当 sed 打开一个新文件时行号没有被重置,所以1 只匹配第一个文件的第一行。

一种解决方案是使用 shell 循环,为每个文件调用一次 sed。 Gumnos' answer 展示了如何以最广泛兼容的方式执行此操作,但如果您有支持 -i 标志的 sed 版本,您可以这样做:

for i in *.txt; do
    sed -i.bak '1d' "$i"
done

可以通过传递一个空的后缀来避免创建备份文件,但我个人认为这不是一件坏事。总有一天你会感激它的!


您似乎没有使用 GNU 工具,但如果是,我建议您使用 GNU awk 来完成此任务。变量FNR 在这里很有用,因为它单独跟踪每个文件的记录号,允许您这样做:

gawk -i inplace 'FNR>1' *.txt

使用inplace 扩展,这允许您从每个文件中删除第一行,只打印FNR 大于1 的行。

测试一下:

$ seq 5 > file1
$ seq 5 > file2
$ gawk -i inplace 'FNR>1' file1 file2
$ cat file1
2
3
4
5
$ cat file2
2
3
4
5

【讨论】:

  • 谢谢你,汤姆。我一定会考虑在未来的任务中使用 awk。 (在查看了一些文档之后,它看起来真的很强大——尽管学习曲线很陡峭)。我也会考虑获得 GNU 实现。再次感谢,伙计。干杯。
【解决方案4】:

您传递给 Sed 的最后一个参数是问题 试试这样的。

var=(`find *txt`)
for file in "${var[@]}"
do
    sed -i -e 1d $file
done

这对我有用。

【讨论】:

  • 完美!这非常有效。感谢您抽出宝贵时间回答问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-15
  • 1970-01-01
  • 2011-03-21
  • 1970-01-01
  • 2013-10-14
  • 2012-05-11
  • 1970-01-01
相关资源
最近更新 更多