2024-08-21 09:00:02

修改多个文件

下面的命令正确地更改了2个文件的内容。

sed -i 's/abc/xyz/g' xaa1 xab1 

但是我需要动态更改几个这样的文件,我不知道文件名。我想写一个命令,将读取当前目录下以xa*开头的所有文件,sed应该改变文件内容。


当前回答

我很惊讶没有人提到-exec参数来查找,这是为这种类型的用例准备的,尽管它将为每个匹配的文件名启动一个进程:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;

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

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'

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

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +

其他回答

您可以同时使用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)

我很惊讶没有人提到-exec参数来查找,这是为这种类型的用例准备的,尽管它将为每个匹配的文件名启动一个进程:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;

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

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'

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

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +

上面有一些很好的答案。我想我要再加上一个简洁且可并行的方法,使用GNU并行,我通常更喜欢xargs:

parallel sed -i 's/abc/xyz/g' {} ::: xa*

将此选项与-j N选项结合起来,可以并行运行N个作业。

如果你能够运行一个脚本,下面是我对类似情况所做的:

使用字典/hashMap(关联数组)和sed命令的变量,我们可以遍历数组来替换几个字符串。在name_pattern中包含一个通配符将允许在指定目录(source_dir)中替换文件中的模式(这可能是类似name_pattern='File*.txt')。 所有更改都写在destin_dir的日志文件中

#!/bin/bash
source_dir=source_path
destin_dir=destin_path
logfile='sedOutput.txt'
name_pattern='File.txt'

echo "--Begin $(date)--" | tee -a $destin_dir/$logfile
echo "Source_DIR=$source_dir destin_DIR=$destin_dir "

declare -A pairs=( 
    ['WHAT1']='FOR1'
    ['OTHER_string_to replace']='string replaced'
)

for i in "${!pairs[@]}"; do
    j=${pairs[$i]}
    echo "[$i]=$j"
    replace_what=$i
    replace_for=$j
    echo " "
    echo "Replace: $replace_what for: $replace_for"
    find $source_dir -name $name_pattern | xargs sed -i "s/$replace_what/$replace_for/g" 
    find $source_dir -name $name_pattern | xargs -I{} grep -n "$replace_for" {} /dev/null | tee -a $destin_dir/$logfile
done

echo " "
echo "----End $(date)---" | tee -a $destin_dir/$logfile

First, the pairs array is declared, each pair is a replacement string, then WHAT1 will be replaced for FOR1 and OTHER_string_to replace will be replaced for string replaced in the file File.txt. In the loop the array is read, the first member of the pair is retrieved as replace_what=$i and the second as replace_for=$j. The find command searches in the directory the filename (that may contain a wildcard) and the sed -i command replaces in the same file(s) what was previously defined. Finally I added a grep redirected to the logfile to log the changes made in the file(s).

这在GNU Bash 4.3 sed 4.2.2中为我工作,并基于VasyaNovikov对Bash中元组循环的回答。

我使用find来完成类似的任务。这很简单:你必须像这样将它作为sed的参数传递:

sed -i 's/EXPRESSION/REPLACEMENT/g' ' find -name "文件。正则表达式”

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