修改多个文件

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

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

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

272631 次浏览

更好的是:

for i in xa*; do
sed -i 's/asd/dfg/g' $i
done

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

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

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

这些命令在Mac OS X自带的默认sed中不起作用。

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.

试着

sed -i '.bak' 's/old/new/g' logfile*

而且

for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done

两者都工作得很好。

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

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

sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')

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

sed -i 's/abc/xyz/g' xa*

这将适用于中等数量的文件,可能是数十个,但可能还不到几百万

我很惊讶没有人提到-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' {} +

你能使

你搜索'xxxx'文本,并将其替换为'yyyy'

grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'

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

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

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

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

使用字典/hashMap(关联数组)和sed命令的变量,我们可以遍历数组来替换几个字符串。在name_pattern中包含通配符将允许用特定目录(source_dir)中的模式(这可能是类似name_pattern='File*.txt'的东西)替换文件中的就地。 所有的更改都写在logfile中的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

首先,声明pair数组,每个pair都是一个替换字符串,然后WHAT1将被替换为FOR1OTHER_string_to replace将被替换为文件File.txt中的string replaced。在循环中读取数组时,数组对的第一个成员被作为replace_what=$i检索,第二个成员被作为replace_for=$j检索。find命令在目录中搜索文件名(可能包含通配符),sed -i命令在相同的文件中替换先前定义的内容。最后,我添加了一个grep重定向到日志文件,以记录文件中所做的更改。

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

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

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

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

银色搜索者解决方案

我为那些不知道银色的探索者(命令行工具是ag))这个神奇工具的人添加了另一个选项。

注意:你可以使用grep和其他工具来做同样的事情,但是Silver Searcher很棒:)

TLDR

ag -l 'abc' | xargs sed -i 's/abc/xyz/g'

安装银色搜索器

sudo apt install silversearcher-ag                # Debian / Ubuntu
sudo pacman -S the_silver_searcher                # Arch / EndeavourOS
sudo yum install epel-release the_silver_searcher # RHEL / CentOS

演示文件

将以下内容粘贴到您的终端以创建一些演示文件:

mkdir /tmp/food
cd /tmp/food
content="Everybody loves to abc this food!"
echo "$content" > ./milk
echo "$content" > ./bread
mkdir ./fastfood
echo "$content" > ./fastfood/pizza
echo "$content" > ./fastfood/burger
mkdir ./fruit
echo "$content" > ./fruit/apple
echo "$content" > ./fruit/apricot

使用“ag)”

下面的ag)命令将递归地找到所有包含字符串'abc'的文件。它会忽略.git目录、.gitignore文件和其他忽略文件:

$ ag 'abc'
milk
1:Everybody loves to abc this food!


bread
1:Everybody loves to abc this food!


fastfood/burger
1:Everybody loves to abc this food!


fastfood/pizza
1:Everybody loves to abc this food!


fruit/apple
1:Everybody loves to abc this food!


fruit/apricot
1:Everybody loves to abc this food!

要列出包含字符串'abc'的文件,使用-l开关:

$ ag -l 'abc'
bread
fastfood/burger
fastfood/pizza
fruit/apricot
milk
fruit/apple

修改多个文件

最后,使用xargs和sed,我们可以用另一个字符串替换'abc'字符串:

ag -l 'abc' | xargs sed -i 's/abc/eat/g'

在上面的命令中,ag列出了所有包含字符串'abc'的文件。xargs命令分割文件名,并将它们分别输送到sed命令中。