【问题标题】:Performing a recursive find and replace with sed only changes first file使用 sed 执行递归查找和替换仅更改第一个文件
【发布时间】:2020-05-01 13:37:44
【问题描述】:

我正在尝试递归搜索当前目录,对找到的每个 .txt 文件的第一行执行 sed 替换。

在 MacOS 上运行这两个命令之一:

find . -name "*.txt" -exec sed -i '' '1 s/([^()]*)//g' {} + 
find . -name '*.txt' -print0 | xargs -0 sed -i '' '1 s/([^()]*)//g'

导致相同的结果。只有找到的“第一个”文件对其执行了sed 操作。这似乎是因为sed -i '' '1 s/([^()]*)//g' 中的1。奇怪的是,即使这仅导致第一个文件被使用,它也仍然只对该文件的第一行执行 sed 替换;它应该这样做。

如果我把命令改成这个

find . -name '*.txt' -print0 | xargs -0 sed -i '' '2 s/([^()]*)//g'

它仍然只是第一个文件被更改,但现在第二行有替换。那么,我的问题是,为什么这似乎只影响返回的第一个文件

find . -name '*.txt' -print0

编辑澄清

我应该通过逐步重新创建问题来澄清我所说的只有“第一个”文件对其执行 sed 操作的确切含义。首先,

这是文件夹层次结构(注意“文件夹 1”中的空格):

.
├── folder\ 1
│   └── test1.txt
├── folder2
│   └── test2.txt
├── folder3
│   └── test3.txt
└── folder4
    └── test4.txt

每个.txt 文件都包含这一行,并且只有这一行:

This should stay (this should go)

当运行上述任一命令时,更改的是文件test2.txt,问题是它是唯一更改的文件!

所以现在,这些文件包含以下内容:

test1.txtThis should stay (this should go)

test2.txtThis should stay

test3.txtThis should stay (this should go)

test4.txtThis should stay (this should go)

我相信这是因为命令的第一部分,例如

find . -name '*.txt' -print0

给出以下内容(每个都由 \0 空字符分隔)

./folder2/test2.txt./folder3/test3.txt./folder4/test4.txt./folder 1/test1.txt

通过随机更改文件夹和文件名,很明显,更改始终是上述\0分隔列表中的第一个文件。

所以问题仍然存在,对 sed 的调用阻止它在所有文件上被调用的原因是什么?

谢谢!

【问题讨论】:

  • 你认为'' 有什么作用?你检查你的 seds 帮助了吗?如果删除它会发生什么?您使用的是哪种 sed,MacO 还是 GNU?
  • @Yunnosch 我认为“”对于防止 MacOS 创建已编辑文件的备份副本是必要的?因此,通过使用'',我强制 sed 按原样编辑每个文件
  • 如果我没看错你使用 MacOs sed,那么我建议 edit 并在你的问题中明确提及。试着找到一个标签来反映这一点。
  • 第一个命令末尾的+是什么意思?我认为如果您改用\;,它将按预期工作。我不确定为什么 xargs 版本不起作用。
  • 谢谢,但在我可以解释它为什么起作用之前,我不会将它作为答案发布。我对xargs 之谜有部分答案...

标签: macos sed find exec xargs


【解决方案1】:

我想关于第一个命令的问题由 Beta 回答,让我回答第二个。

尝试将-t(测试)选项放到xargs,看看命令行如何 展开:

find . -name '*.txt' -print0 | xargs -0 -t sed -i '' '1 s/([^()]*)//g'

它会输出如下内容:

sed -i '' 1 s/([^()]*)//g ./test1.txt ./test2.txt ./test3.txt ./test4.txt

xargs的默认行为是执行指定的命令 (在这种情况下为sed)同时从标准中读取所有参数 输入。
此外,sed 不会跨多个输入文件重置行号,s 命令 以上仅适用于第一个文件。

您可以使用-l1 选项更改xargs 的行为:

find . -name '*.txt' -print0 | xargs -0 -l1 -t sed '1 s/([^()]*)//g'

输出:

sed -i '' 1 s/([^()]*)//g ./test1.txt
sed -i '' 1 s/([^()]*)//g ./test2.txt
sed -i '' 1 s/([^()]*)//g ./test3.txt
sed -i '' 1 s/([^()]*)//g ./test4.txt

然后sed 将按预期工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 2012-12-30
    • 2013-05-26
    • 2013-04-01
    • 2020-07-04
    • 2017-12-20
    • 2020-08-05
    相关资源
    最近更新 更多