使用 bash 脚本替换多个文件中的密码时转义特殊字符

Escape special characters when replacing passwords in multiple files with a bash-script

我发现了很多封闭的问题,但并不完全适合我的用例,因此不得不打开一个新问题。

猜猜一个系统的密码由多个配置文件中的所有大小写字母、数字和特殊字符(a-z、A-Z、0-9、#)组成(Web 应用程序使用相同的密码)在主域、子域和所有相关数据库上)。带有密码的此类文件的示例为:

$databases = array (
  'default' =>
  array (
    'default' =>
    array (
      'database' => 'database_name',
      'username' => 'datbase_user',
      'password' => 'yv[48k2)KMGx{g1b',
      'host' => 'localhost',
      'port' => '',
      'driver' => 'mysql',
      'collation' => 'utf8mb4_general_ci',
      'charset' => 'utf8mb4',
      'prefix' => '',
    ),
  ),
);

我正在尝试编写一个 bash 代码,它接受自动生成的新密码(使用相同的算法 - 所有小写和大写字母、数字和特殊字符),找到所有出现的旧密码在各自父域及其子域的主目录中,并替换为新密码。以下单行工作正常:

grep -rlF "$old_pass" /path/to/directory | xargs sed -i "s/$old_pass/$new_pass/g"

对于仅由字母和数字组成的密码(例如 qVYYgB5bRxUYpHg)。然而,虽然 grep 部分工作正常,但 sed 部分一旦系统将特殊字符添加到生成的密码(例如 aN#zDU>lve15Uwqn)给出:

sed -i "s/$old_pass/$new_pass/g" password.php
sed: -e expression #1, char 37: unterminated `s' command

替换所有现有密码的最佳方法是什么,该密码由多个文件中不同变量中存储的所有字母、数字和特殊字符组成?

你的 sed 表达式是错误的,因为你需要转义方括号 [:

sed -i 's/yv\[48k2)KMGx{g1b/aN#zDU>lve15Uwqn/g' password.php

为避免 shell 参数扩展,请在 sed 表达式中使用单引号。

如果要匹配所有字母、数字和特殊字符(假设标点符号),可以使用class个字符:

sed 's/[[:punct:][:alnum:]]*/NEWPASSWORKD/g' password.php

我认为自动生成的密码的可能排列可能超出了您将能够轻松适应 sed 的范围,但问题的症结在于保护元字符。对于这个具体的例子:

$: echo "$b"              # the desired string
aN#zDU>lve15Uwqn
$: echo "$a"              # the current string, with embedded metachars
yv[48k2)KMGx{g1b
$: echo "$a" |            # pipe it to a sed to clean it
>    sed 's/\W/\&/g'     # backquote all non-word chars
yv\[48k2\)KMGx\{g1b
$: echo "$a" | sed 's/\W/\&/g'>a # save to a file
$: cat a
yv\[48k2\)KMGx\{g1b
$: echo "sed -E 's/$(<a)/$b/g'">x # save the sed to be run to a file
$: cat x
sed -E 's/yv\[48k2\)KMGx\{g1b/aN#zDU>lve15Uwqn/g'
$: echo $a | . x          # source it
aN#zDU>lve15Uwqn

并非完全有必要将所有这些保存到文件中,但它可以让您更轻松地管理您的报价。正如 Ed 在下面提到的,您基本上必须为新密码做同样的事情。

所以你的实际脚本:

 echo "$old_pass" | sed 's/\W/\&/g'> old_pat
 echo "$new_pass" | sed 's/\W/\&/g'> new_pat
 echo "sed -E 's/$(<old_pat)/$(<new_pat)/g'" > src # save the command to a file
 echo "$old_pass" | . src                        # source it

我没有包含 -i 来进行就地编辑,因为您需要先检查该过程。

希望这对某人有所帮助,但总的来说,这是其中一种情况,即使我可能会使用 awk... 或者更可能的是,老实说,只是重构逻辑。 :)

...您不会在 php 文件中对所有这些密码进行硬编码,对吗? :D

最好在此处使用 perl,因为它支持 /\Q...\E/ 正则表达式模式,可以处理这些标签中的所有字母 字面意思(没有特殊的正则表达式含义) .

这是一个例子:

cat file

abc123
pass=yv[48k2)KMGx{g1b
foobar

现在使用这个 perl 命令:

old_pass='yv[48k2)KMGx{g1b'
new_pass='aN#zDU>lve15Uwqn'

perl -pe "s~\Q${old_pass}\E~\Q$new_pass~g" file

abc123
pass=aN\#zDU\>lve15Uwqn
foobar

如果您没有 perl,那么您可以使用这个 awk,它使用 sting 搜索而不使用任何正则表达式:

awk -v old="$old_pass" -v new="$new_pass" 'n=index([=13=], old) { 
   [=13=] = substr([=13=], 1, n-1) new substr([=13=], n + length(old)) } 1' file