【问题标题】:Escape special characters when replacing passwords in multiple files with a bash-script使用 bash 脚本替换多个文件中的密码时转义特殊字符
【发布时间】:2018-11-06 08:03:29
【问题描述】:

我发现了很多很接近的问题,但不完全适合我的用例,所以不得不打开一个新问题。

猜一个系统的密码由多个配置文件中的所有小写和大写字母、数字和特殊字符(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 部分工作正常,但只要系统向生成的密码(例如aN#zDU>lve15Uwqn)添加特殊字符,sed 部分就会失败,给出:

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

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

【问题讨论】:

  • 您现在发布的是一个非常不同的问题,对迄今为止发布的任何答案都有非常不同的正确解决方案(提示:您不会搜索原始密码)。我不想再去,但希望其他人会。祝你好运。
  • 对不起,Ed,我一开始以为问题只是sed 部分所以决定集中注意力,但是当人们开始建议操作给定文件时,我不得不解释我不' t 有特定的文件,而是 grep 搜索的输出。
  • 配置文件是否有一个通用名称(例如config.txt)或通用后缀(例如foo,cfgbar.cfg等)或任何其他通过名称区分它们的方式其他文件?
  • 最终用户的应用程序可以有任何类型的文件,所以很遗憾我不能依赖文件名中的任何模式,而是使用 grep 搜索然后操作文件。
  • 不过,这是一种有缺陷的方法 - 您依赖的旧密码字符串不是某个较长字符串的一部分,也不存在于任何其他文件中。这样做可能会严重损坏文件。您至少应该测试' 分隔符和单词password。我至少会搜索'password' => 'yv[48k2)KMGx{g1b',,而不仅仅是yv[48k2)KMGx{g1b,或者我可能实际上正在搜索'password' => '[^']+',(也许$databases = array以确保我使用正确的文件类型)除非实际的旧密码是必需的。

标签: regex awk sed special-characters


【解决方案1】:

最好在此处使用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($0, old) { 
   $0 = substr($0, 1, n-1) new substr($0, n + length(old)) } 1' file

【讨论】:

  • 如果密码包含反斜杠,则 awk 脚本将失败,因此 my use of ENVIRON。我通常不会狡辩,但因为这个问题专门关于包含“特殊”字符的变量......
  • 哦,是的,没错,一些反斜杠可能会导致 -v 出现问题
  • perl 的例子输出了想要的结果,但是文件没有保存。我如何使用 perl 进行编辑?附加 > new_file 然后覆盖旧文件有效,但我希望有一个更短的版本。
  • 请注意,我必须像grep -rlF "$old_pass" | xargs perl -pe "s~\\Q${old_pass}\\E~\\Q$new_pass~g"一样将perl命令附加到grep,所以命令行上不能提供文件名。
  • 您的回答最接近于缩小对stackoverflow.com/a/53170714/5797450的预期效果@谢谢!
【解决方案2】:

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

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

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

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

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

【讨论】:

  • 对不起,我没有提到密码实际上存储在变量中,所以我必须使用双引号。 sed "s/[[:punct:][:alnum:]]*yv[48k2)KMGx{g1b/aN#zDU>lve15Uwqn/g" password.php 没用。
  • 正如我已经提到的,如果你使用方括号[,你必须用反斜杠转义字符。
  • 您的示例完全错过了旧密码。请给出一个有效的答案。
【解决方案3】:

我认为自动生成密码的可能排列可能超出了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

【讨论】:

  • 您还需要清理b 以适应&amp;\1/ 等,并且您需要添加代码来处理's(也许映射它到某个八进制值或使用" 定界符,这样你就可以逃避那些,但你需要在其他地方加倍转义或我们[]?)。可能还有其他人。或者换一种说法 - 甚至不要...... :-)。
  • 我倾向于同意,Ed。这不是我在 sed 中会做的事情。给出的示例的解决方案无法处理所有问题。
  • 是的。 “只是不解析这个”选项会非常好,哈哈
  • 对 - sed --mc-hammer ....
  • 哈哈!!我差点从椅子上摔下来。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-18
  • 2017-10-19
  • 1970-01-01
  • 2012-11-21
  • 2021-11-03
  • 2013-12-23
相关资源
最近更新 更多