以下命令正确更改了2个文件的内容。
sed -i 's/abc/xyz/g' xaa1 xab1
但是我需要做的是动态地更改几个这样的文件,我不知道文件名。我想写一个命令,从当前目录开始读取所有文件,xa*
并且sed
应该更改文件内容。
以下命令正确更改了2个文件的内容。
sed -i 's/abc/xyz/g' xaa1 xab1
但是我需要做的是动态地更改几个这样的文件,我不知道文件名。我想写一个命令,从当前目录开始读取所有文件,xa*
并且sed
应该更改文件内容。
Answers:
更好的是:
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)
#
for
。为了保护自己免受find ... | xargs ...
for
比它的echo
还是grep
?
"$i"
而不是$i
来避免带空格的文件名拆分单词。否则,这是非常好的。
for
是语言语法的一部分,甚至不仅仅是内置语法。对于sed -i 's/old/new' *
,*
必须将sall 的扩展作为arglist传递给sed,而且我很确定,这必须在sed
进程启动之前进行。使用for
循环,完整的arglist(的扩展名*
)永远不会作为命令传递,仅存储在shell内存中并进行迭代。我对此完全没有任何参考,这似乎是有区别的。(我很想听听其他知识渊博的人的话...)
我很惊讶没有人提到要查找的-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变量而不是;
in find来允许find为每个子进程调用提供多个文件:
find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +
find ./ -type f -name 'xa*' -exec sed -i '' 's/asd/dsg/g' {} \;
这是find命令的位置,后跟OSX ./
的一对单引号-i
。
./
等于.
和-i
仅具有backupsuffix参数。
-exec
find和find 的选项{} +
足以解决所述问题,并且对于大多数需求来说应该是不错的选择。但是xargs
通常是更好的选择,因为它还允许与该-p
选项并行处理。当您的全局扩展足够大以至于使命令行长度溢出时,您可能还会从顺序运行的加速中受益。
您可以一起使用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)
grep -v
避免git的文件夹grep -rl <old> . | grep -v \.git | xargs sed -i 's/<old>/<new>/g'
这些命令在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.
试过了
sed -i '.bak' 's/old/new/g' logfile*
和
for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done
两者都很好。
@PaulR将此作为评论发布,但是人们应该将其视为答案(并且该答案最适合我的需求):
sed -i 's/abc/xyz/g' xa*
这将适用于中等数量的文件,可能大约几十个文件,但可能不会数百万个文件。
sed -i 's|auth-user-pass nordvpn.txt|auth-user-pass /etc/openvpn/nordvpn.txt|g' *.ovpn
。
另一种更通用的方法是使用find
:
sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')
我正在find
执行类似的任务。这很简单:您必须sed
像这样将其作为参数传递:
sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`
这样,您不必编写复杂的循环,并且很容易看到要更改的文件,而仅在运行find
之前运行sed
。
你可以做
' xxxx '文本u搜索,并将其替换为' yyyy '
grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'
如果您能够运行脚本,这是我针对类似情况所做的操作:
使用字典/ hashMap(关联数组)和sed
命令变量,我们可以遍历数组以替换几个字符串。在中包含通配符name_pattern
将允许name_pattern='File*.txt'
使用特定目录(source_dir
)中的模式(例如)替换文件中的原位。所有的变化都写在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
首先,声明pairs数组,每个对都是一个替换字符串,然后WHAT1
将被替换为FOR1
并且OTHER_string_to replace
将string replaced
在file中被替换为File.txt
。在循环中,读取数组,该对的第一个成员检索为replace_what=$i
,第二个检索为replace_for=$j
。该find
命令在目录中搜索文件名(可能包含通配符),并且该sed -i
命令将替换先前定义的文件。最后我加了一个grep
日志文件重定向,以记录在文件中所做的更改。
这对我GNU Bash 4.3
sed 4.2.2
有用,并基于VasyaNovikov对bash中的元组进行循环的答案 。
sed -i 's/abc/xyz/g' xa*
?