本文介绍了Bash - 搜索和替换操作,报告已更改的文件和行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我有一个输入文件test.txt",如下所示 -
hostname=abc.com hostname=xyz.comdb-host=abc.com db-host=xyz.com
在每一行中,空格前的值是旧值,需要在名为test"的文件夹中递归替换空格后的新值.我可以使用下面的 shell 脚本来做到这一点.
#!/bin/bashIFS=$'
'对于 `cat test.txt` 中的 f做OLD=$(echo $f| cut -d ' ' -f 1)echo "旧 = $OLD"NEW=$(echo $f| cut -d ' ' -f 2)echo "新 = $NEW"查找测试类型 f |xargs sed -i.bak "s/$OLD/$NEW/g"完毕
sed"在 100 个文件中动态替换字符串.
是否有技巧或替代方法可以让我获得文件更改的报告,例如文件的绝对路径&更改的确切行?
PS - 我知道 sed 或流编辑器不支持开箱即用的此功能.我不想使用版本控制,因为这对这项任务来说太过分了.
解决方案
让我们从简单的脚本重写开始,让它在处理更广泛的替换值时更加健壮一点,但也更快:
>
#!/bin/bash# 为 sed 转义正则表达式和替换字符串escapeRegex() { sed 's/[^^]/[&]/g;s/^/\^/g' <<<"$1";}escapeSubst() { sed 's/[&/]/\&/g' <<<"$1";}while read -r old new;做find test -type f -exec sed "/$(escapeRegex "$old")/$(escapeSubst "$new")/g" -i '{}' ;完成 <test.txt
因此,我们在 test.txt
的行中循环遍历以空格分隔的字段对(old
、new
)并运行标准sed
就地替换用 find
找到的所有文件.
与您的脚本非常相似,但我们正确读取了测试中的行
.txt(无分词、路径名/变量扩展等),我们尽可能使用 Bash 内置函数(无需调用外部工具,如 cat
、cut
, xargs
);我们转义sed
元字符 在 old
/new
值中正确用作 sed
的正则表达式和替换表达式.
现在让我们添加 从 sed 记录::>
#!/bin/bash# 为 sed 转义正则表达式和替换字符串escapeRegex() { sed 's/[^^]/[&]/g;s/^/\^/g' <<<"$1";}escapeSubst() { sed 's/[&/]/\&/g' <<<"$1";}while read -r old new;做find test -type f -printf '
[%p]
' -exec sed "/$(escapeRegex "$old")/{Hs//$(escapeSubst "$new")/gHXs/
/-->/带/开发/标准输出X}" -i '{}' > >(tee -a change.log) ;完成 <test.txt
上面的 sed
脚本将每个 old
更改为 new
,但它也会写入 old -->新的
行到/dev/stdout
(特定于Bash),我们依次将其附加到change.log
文件.-printf
操作在 find
输出一个带有文件名的标题"行,为每个处理的文件.
这样,您的更改日志"将如下所示:
[file1]主机名=abc.com -->主机名=xyz.com[文件 2][文件1]db-host=abc.com -->db-host=xyz.com[文件 2]db-host=abc.com -->db-host=xyz.com
为了完整起见,快速浏览 sed
脚本.我们只对包含 old
值的行进行操作.对于每一个这样的行,我们将它存储到保持空间 (h
),将其更改为 new
,将该新值附加到保持空间(与换行符,H
) 现在保存 oldnew
.我们用模式空间 (x
) 交换保持,所以我们可以运行 s
命令将其转换为 old -->新的
.在使用 w
将其写入 stdout
之后,我们将 new
从保持状态移回模式空间,因此它被写入(就地)到处理的文件.
I have a input file "test.txt" as below -
hostname=abc.com hostname=xyz.com
db-host=abc.com db-host=xyz.com
In each line, the value before space is the old value which needs to be replaced by the new value after the space recursively in a folder named "test". I am able to do this using below shell script.
#!/bin/bash
IFS=$'
'
for f in `cat test.txt`
do
OLD=$(echo $f| cut -d ' ' -f 1)
echo "Old = $OLD"
NEW=$(echo $f| cut -d ' ' -f 2)
echo "New = $NEW"
find test -type f | xargs sed -i.bak "s/$OLD/$NEW/g"
done
"sed" replaces the strings on the fly in 100s of files.
Is there a trick or an alternative way by which i can get a report of the files changed like absolute path of the file & the exact lines that got changed ?
PS - I understand that sed or stream editors doesn't support this functionality out of the box. I don't want to use versioning as it will be an overkill for this task.
解决方案
Let's start with a simple rewrite of your script, to make it a little bit more robust at handling a wider range of replacement values, but also faster:
#!/bin/bash
# escape regexp and replacement strings for sed
escapeRegex() { sed 's/[^^]/[&]/g; s/^/\^/g' <<<"$1"; }
escapeSubst() { sed 's/[&/]/\&/g' <<<"$1"; }
while read -r old new; do
find test -type f -exec sed "/$(escapeRegex "$old")/$(escapeSubst "$new")/g" -i '{}' ;
done <test.txt
So, we loop over pairs of whitespace-separated fields (old
, new
) in lines from test.txt
and run a standard sed
in-place replace on all files found with find
.
Pretty similar to your script, but we properly read lines from test.txt
(no word splitting, pathname/variable expansion, etc.), we use Bash builtins whenever possible (no need to call external tools like cat
, cut
, xargs
); and we escape sed
metacharacters in old
/new
values for proper use as sed
's regexp and replacement expressions.
Now let's add logging from sed:
#!/bin/bash
# escape regexp and replacement strings for sed
escapeRegex() { sed 's/[^^]/[&]/g; s/^/\^/g' <<<"$1"; }
escapeSubst() { sed 's/[&/]/\&/g' <<<"$1"; }
while read -r old new; do
find test -type f -printf '
[%p]
' -exec sed "/$(escapeRegex "$old")/{
h
s//$(escapeSubst "$new")/g
H
x
s/
/ --> /
w /dev/stdout
x
}" -i '{}' > >(tee -a change.log) ;
done <test.txt
The sed
script above changes each old
to new
, but it also writes old --> new
line to /dev/stdout
(Bash-specific), which we in turn append to change.log
file. The -printf
action in find
outputs a "header" line with file name, for each file processed.
With this, your "change log" will look something like:
[file1]
hostname=abc.com --> hostname=xyz.com
[file2]
[file1]
db-host=abc.com --> db-host=xyz.com
[file2]
db-host=abc.com --> db-host=xyz.com
Just for completeness, a quick walk-through the sed
script. We act only on lines containing the old
value. For each such line, we store it to hold space (h
), change it to new
, append that new value to the hold space (joined with newline, H
) which now holds oldnew
. We swap hold with pattern space (x
), so we can run s
command that converts it to old --> new
. After writing that to the stdout
with w
, we move the new
back from hold to pattern space, so it gets written (in-place) to the file processed.
这篇关于Bash - 搜索和替换操作,报告已更改的文件和行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!