更改多个文件

sha*_*nuo 173 sed

以下命令正在更改2个文件的内容.

sed -i 's/abc/xyz/g' xaa1 xab1 
Run Code Online (Sandbox Code Playgroud)

但我需要做的是动态更改几个这样的文件,我不知道文件名.我想编写一个命令,它将从当前目录开始读取所有文件,xa*并且sed应该更改文件内容.

eal*_*nso 142

我很惊讶没有人提到查找的-exec参数,它适用于这种用例,尽管它会为每个匹配的文件名启动一个进程:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;
Run Code Online (Sandbox Code Playgroud)

或者,可以使用xargs,这将调用更少的进程:

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'
Run Code Online (Sandbox Code Playgroud)

或者更简单地使用+ exec变体而不是;find来允许find为每个子进程调用提供多个文件:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +
Run Code Online (Sandbox Code Playgroud)

  • 我不得不在这个答案中修改命令:`find ./ -type f -name'xa*'-exec sed -i''s/asd/dsg/g'{} \;`这是的位置对于OSX,查找命令`./`和`-i`之后的一对单引号. (6认同)
  • find 的“-exec”选项与“{} +”一起足以解决上述问题,并且应该可以满足大多数要求。但总的来说,“xargs”是更好的选择,因为它还允许使用“-p”选项进行并行处理。当您的全局扩展大到足以溢出命令行长度时,您也可能受益于顺序运行的加速。 (2认同)

len*_*nik 125

更好的是:

for i in xa*; do
    sed -i 's/asd/dfg/g' $i
done
Run Code Online (Sandbox Code Playgroud)

因为没有人知道有多少文件,并且很容易打破命令行限制.

以下是文件太多时会发生的情况:

# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#
Run Code Online (Sandbox Code Playgroud)

  • 如果有那么多文件,你将打破`for`命令中的命令行限制.为了保护自己,你必须使用`find ... | xargs ......` (15认同)
  • "当然"??证明给我看.文档在哪里? (6认同)
  • 在sed命令中,你需要使用`"$ i"`而不是`$ i`来避免在带空格的文件名上进行单词拆分.否则这非常好. (5认同)
  • 看到更新的答案.如果您需要更多信息,请提出正式问题,以便人们可以帮助您. (4认同)
  • 关于列表,我认为不同之处在于`for`是语言语法的一部分,甚至不仅仅是内置语言.对于`sed -i's/old/new'*`,`*`的扩展必须作为arglist传递给sed,我很确定这必须在`sed`进程之前发生开始.使用`for`循环,完整的arglist(`*`的扩展)永远不会作为命令传递,只存储在shell内存中并迭代通过.我根本没有任何参考,但似乎可能是差异.(我很想听听更有知识的人...) (3认同)

Raj*_*Raj 77

你可以一起使用grep和sed.这允许您递归搜索子目录.

Linux: grep -r -l <old> * | xargs sed -i 's/<old>/<new>/g'
OS X: grep -r -l <old> * | xargs sed -i '' 's/<old>/<new>/g'

For grep:
    -r recursively searches subdirectories 
    -l prints file names that contain matches
For sed:
    -i extension (Note: An argument needs to be provided on OS X)
Run Code Online (Sandbox Code Playgroud)

  • 对我来说,这种方法的好处是我可以使用`grep -v`来避免git文件夹`grep -rl <old>.| grep -v\.git | xargs sed -i's/<old>/<new>/g'` (3认同)

fun*_*oll 28

这些命令不适sed用于Mac OS X附带的默认设置.

来自man 1 sed:

-i extension
             Edit files in-place, saving backups with the specified
             extension.  If a zero-length extension is given, no backup 
             will be saved.  It is not recommended to give a zero-length
             extension when in-place editing files, as you risk corruption
             or partial content in situations where disk space is exhausted, etc.
Run Code Online (Sandbox Code Playgroud)

试着

sed -i '.bak' 's/old/new/g' logfile*
Run Code Online (Sandbox Code Playgroud)

for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done
Run Code Online (Sandbox Code Playgroud)

两者都很好.

  • @sumek这是OS X上的示例终端会话,显示sed替换所有出现的内容:[GitHub Gist](https://gist.github.com/funroll/5504098) (2认同)

pal*_*wim 15

@PaulR将此作为评论发布,但人们应将其视为答案(此答案最适合我的需求):

sed -i 's/abc/xyz/g' xa*
Run Code Online (Sandbox Code Playgroud)

这适用于适量的文件,可能大约为数十,但可能不是数百万.


dki*_*zer 7

另一种更通用的方法是使用find:

sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')
Run Code Online (Sandbox Code Playgroud)

  • 那明显是错的.在上面的命令中,$(find....)被扩展为单个命令,如果有许多匹配的文件,这个命令可能会很长.如果它太长(例如在我的系统中,限制大约是2097152个字符),则可能会出现错误:"参数列表太长",命令将失败.请谷歌此错误以获得一些背景信息. (3认同)

小智 5

我正在用于find类似的任务。这很简单:你必须将它作为参数传递,sed如下所示:

sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`

这样你就不必编写复杂的循环,而且很容易看出,你要更改哪些文件,只需在find运行之前运行即可sed

  • 这与[@dkinzer的答案](/sf/answers/1949494571/)完全相同。 (2认同)