在终端中使用 mysql 来抑制警告消息,但密码是用 bash 脚本编写的

Posted

技术标签:

【中文标题】在终端中使用 mysql 来抑制警告消息,但密码是用 bash 脚本编写的【英文标题】:Suppress warning messages using mysql from within Terminal, but password written in bash script 【发布时间】:2014-01-12 03:11:43 【问题描述】:

当我尝试从终端中在 mysql 上运行以下命令时:

mysql -u $user -p$password -e "statement"

执行按预期工作,但它总是发出警告:

警告:在命令行界面上使用密码可能不安全。

但是,我必须使用存储我的密码的环境变量 ($password) 执行上面的语句,因为我想在终端内的 bash 脚本中迭代地运行命令,我绝对不喜欢这个想法等待提示出现并强迫我在单个脚本中输入密码 50 或 100 次。所以这是我的问题:

抑制警告是否可行?正如我所说,该命令可以正常工作,但是当我循环运行该命令 50 或 100 次时,窗口变得非常混乱。

我是否应该遵守警告信息并且不要在脚本中写入密码?如果是这种情况,那么每次提示强制我输入密码时我都必须输入密码吗?

运行man mysql 无济于事,只说

--show-warnings 如果有任何语句,则在每个语句之后显示警告。此选项适用于交互和批处理模式。

并没有提及如何关闭功能,如果我没有遗漏什么。

我在 OS X 10.9.1 Mavericks 上使用自制软件的 MySQL 5.6。

【问题讨论】:

推荐的方法是将您的密码存储在一个选项文件中(smth like [client] password=my_password in ~/.my.cnf)。当然,它也有一些安全隐患,但至少 任何人 都无法访问它,因为他们可以运行 ps,并且您可以通过文件权限控制它。 mysql -u root password root -e "statement" > /dev/null ? 对了,你也可以使用 Python pexcept 之类的东西。它可以进行终端插入,也可以处理命令给出的反馈。这样你就可以跳过那个详细的输出并去掉你想要的实际输出:) IMO 推荐的方式是惩罚做正确事情的人以保护做错事情的人。如果密码存储在脚本文件中,它将不会显示在 ps 或任何日志中。这是做到这一点的正确方法。将文件放在外部文件中确实有助于那些会 cron 密码但开始时不好的人。与此同时,已经运行多年的脚本现在失败了,我们需要修改它们,因为这个警告出现在标准错误中。 关于不明文存储密码的推荐方法,详见dev.mysql.com/doc/refman/5.6/en/mysql-config-editor.html 【参考方案1】:

如果您的 MySQL 客户端/服务器版本是 5.6.x,那么避免 WARNING 消息的方法是使用 mysql_config_editor 工具:

mysql_config_editor set --login-path=local --host=localhost --user=username --password

然后你可以在你的shell脚本中使用:

mysql --login-path=local  -e "statement"

代替:

mysql -u username -p pass -e "statement"

【讨论】:

请记住,--login-path 必须位于所有其他参数之前。我正在尝试mysqldump --tables --login-path=local 并收到错误unknown variable 'login-path=local' 如果我们直接执行 shell 文件,这可以正常工作,但如果从 crontab 调用则无法正常工作 @NabeelArshad 我认为这是因为在您的 crontab 中未设置用户的“家”(通常为 ENV 变量),因此在 crontab 中客户端无法找到正确的 ~/ .mylogin.cnf @NamGVU,我不确定,但是,我相信这个解决方案存储了加密的密码。 那么在所有这些 cmets 之后,对于从 crontab 调用的脚本有效的解决方案是什么?【参考方案2】:

我使用类似的东西:

mysql --defaults-extra-file=/path/to/config.cnf

mysqldump --defaults-extra-file=/path/to/config.cnf 

config.cnf 包含的位置:

[client]
user = "whatever"
password = "whatever"
host = "whatever"

这允许您拥有多个配置文件 - 用于不同的服务器/角色/数据库。使用 ~/.my.cnf 只会让您拥有一组配置(尽管它可能是一组有用的默认值)。

如果您使用的是基于 Debian 的发行版,并且以 root 身份运行,您可以跳过上述步骤,直接使用 /etc/mysql/debian.cnf 进入...:

mysql --defaults-extra-file=/etc/mysql/debian.cnf

【讨论】:

注意:--defaults-extra-file 必须是第一个选项,否则 mysql 会报错 mysqldump: unknown variable 'defaults-extra-file 已接受答案的绝佳替代方案。绝对不要设置MYSQL_PWD 变量.... 对于 5.6 以下的版本绝对是一个不错的选择。否则,我会选择接受的答案。 创建临时 .cnf 文件的另一种方法是在 Bash 中执行此操作:mysql --defaults-extra-file=<(printf "[client]\nuser = %s\npassword = %s" "$user" "$pwd") -e "statement"。由于printf 是由 Bash 直接执行的,因此它不会出现在 ps 中。 我需要使用--defaults-file 而不是--defaults-extra-file,因为后者优先考虑~/.my.cnf 中的设置。【参考方案3】:

一种方便(但同样不安全)的方法是使用:

MYSQL_PWD=xxxxxxxx mysql -u root -e "statement"

请注意,官方文档建议不要这样做。 见6.1.2.1 End-User Guidelines for Password Security (Mysql Manual for Version 5.6):

将您的密码存储在MYSQL_PWD 环境变量中

这种指定 MySQL 密码的方法必须被视为极其不安全,不应使用。 ps 的某些版本包含一个选项来显示正在运行的进程的环境。在某些系统上,如果您设置MYSQL_PWD,则您的密码会暴露给运行ps 的任何其他用户。即使在没有这种 ps 版本的系统上,假设没有其他方法可供用户检查进程环境也是不明智的。

【讨论】:

这在我的 bash 脚本中不起作用:Access denied for user 'root'@'localhost' (using password: NO) 在脚本中,需要export MYSQL_PWD=whatever 因为我的查询速度很快,所以选择了这个选项。然后我在执行查询后将其设置为虚假的。 在脚本中不需要运行export,只需将其全部放在一行:MYSQL_PWD=xxxxxxxx mysql -u root -e "statement" MYSQL_PWD 记录在 5.5、5.6 和 5.7 客户端文档中,但从 8.0 文档中省略。【参考方案4】:

如果您希望在命令行中使用密码,我发现这可以过滤掉特定的错误消息:

mysqlcommand 2>&1 | grep -v "Warning: Using a password"

它基本上是将标准错误重定向到标准输出——并使用 grep 删除与“警告:使用密码”匹配的所有行。

这样,您可以看到任何其他输出,包括错误。我将它用于各种 shell 脚本等。

【讨论】:

这是一个出色的解决方案,可用于在其他任务中调用的单线任务,例如为 Capistrano 部署创建 Rake 任务。 静音该消息非常简单,无需在 MySQL 设置中进行任何操作。 我如何将它与 mysqldump 一起使用,我已经有一个 SQL 重定向? 与这里的其他解决方案相比,这是一个非常糟糕的解决方案。如果文本更改,它可能会在任何后续版本的 MySQL 中失败,也可能无法在其他语言环境中工作。 如何将错误重定向回标准错误? (例如1>&2【参考方案5】:

以下是我如何让我的 bash 脚本让我的日常 mysqldump 数据库备份更安全地工作。这是 Cristian Porta 伟大的answer 的扩展。

    首先使用mysql_config_editor(mysql 5.6+自带)设置加密密码文件。假设您的用户名是“db_user”。从 shell 提示符运行:

     mysql_config_editor set --login-path=local --host=localhost --user=db_user --password
    

    它提示输入密码。输入后,用户/密码将加密保存在您的home/system_username/.mylogin.cnf

    当然,将“system_username”更改为您在服务器上的用户名。

    从此更改您的 bash 脚本:

     mysqldump -u db_user -pInsecurePassword my_database | gzip > db_backup.tar.gz
    

    到这里:

     mysqldump --login-path=local my_database | gzip > db_backup.tar.gz
    

没有更多暴露的密码。

【讨论】:

【参考方案6】:

最简单的方法是

mysql -u root -pMYPASSWORD -e "show databases" 2>/dev/null

【讨论】:

问题是这也会抑制脚本中的合法错误。 您也可以创建一个日志文件 2>/var/log/myscript.log 来记录该错误。 使用2>/dev/null | grep -v "mysql: [Warning] Using a password on the command line interface can be insecure." 仅显示有问题的警告 自 2 起>/dev/null 已经重定向到 /dev/null - grep 在那里没有做任何事情。重定向需要转到 2>&1 来过滤掉这个单一的警告。【参考方案7】:

这很简单。这对我有用。

export MYSQL_PWD=password; mysql --user=username -e "statement"

MYSQL_PWD 是environment variables that are used directly or indirectly by MySQL 之一。

来自文档:

MYSQL_PWD - 连接到 mysqld 时的默认密码。使用它是不安全的。见Section 6.1.2.1, “End-User Guidelines for Password Security”。

【讨论】:

这是这里唯一正确的答案!像魅力一样工作:) 不推荐使用 MYSQL_PWD 环境变量来指定 MySQL 密码。【参考方案8】:

好的,没有临时文件或任何东西的解决方案:

mysql --defaults-extra-file=<(echo $'[client]\npassword='"$password") -u $user -e "statement"

与其他人提到的类似,但这里不需要实际文件,这部分命令伪造文件:&lt;(echo ...) (注意@987654323中间没有空格@

【讨论】:

如果您的~/. 中已有带有密码条目的.my.cnf 文件,则此方法无效 我认为这并没有得到任何 cmets 相反,它是相对安全的。如果是这样的话,我认为这是一个很棒的解决方法。 @stepanian mysql[dump] 报告的安全问题源于密码将出现在正在运行的进程列表中(参见ps -ef | grep mysql)。如果您将它放在该语句中所示的变量中,则可能没问题。但是,如果您的密码是明文而不是$password,那也不会好多少(回显会非常快,因此作为黑客,在这种情况下更难获取信息,但仍有可能)。【参考方案9】:

您也可以在脚本中运行mysql_config_editor,在指定登录路径时传入密码

expect -c "
spawn mysql_config_editor set --login-path=$mySqlUser --host=localhost --user=$mySqlUser --password
expect -nocase \"Enter password:\" send \"$mySqlPassword\r\"; interact
"

这会启动一个期望会话,可以在脚本中使用它来与提示交互

See this post

【讨论】:

那个“>”真的应该在那里吗? yes '>' 是为了让 mysql_config_editor 从 stdout 获取输入。从 stdout 传递到 mysql_config_editor 的是您希望该用户拥有的密码 如果没有“>”,那么会发生 echo 命令被解析,您将看到的只是 echo 命令之后的所有内容 那么,您的意思是使用管道运算符“|”,对吗?至少,在 *nix 和 DOS 中,“>”会捕获 STDOUT 并将其写入当前工作目录中名为“mysql_config_editor”的文件中。 是的,你是对的,我已经编辑了我原来的答案【参考方案10】:

一个简单的工作脚本。将此命名为“mysql”,并将其放在“/usr/bin”之前的路径中。其他命令的明显变体,或者警告文本是否不同。

#!/bin/sh

(
(
(
(
(
    /usr/bin/mysql "$@"
) 1>&9 
) 2>&1
) | fgrep -v 'mysql: [Warning] Using a password on the command line interface can be insecure.'
) 1>&2 
) 9>&1

【讨论】:

虽然这个答案有点令人费解,但我只是赞成它,因为看到没有明确错误的答案或在没有 cmets 的情况下被拒绝投票有点不合常规,这让我非常讨厌!难怪大卫没有回答任何其他问题!他跳了进去,试图帮助一个新颖的解决方案,却被猛烈抨击,没有解释为什么!不离开 cmets 的 FU 匿名投票者! +1 同意杰里米·戴维斯。这是令人费解的,但如果没有其他选择,这可能是可以接受的。这绝对没有错,不像关闭警告,这是有史以来最愚蠢的想法! @JeremyDavis 是的,这很复杂,主要是因为我想展示作品。它可能没有任何括号就可以完成,但可能不太清楚。这也是我在所有堆栈交换中的第一个非阅读活动......之后我很长时间没有碰它。非常感谢您的评论。 @DavidG。 - 很高兴我的评论对你有价值。很高兴看到你回来了,你的答案现在是积极的。就个人而言,我认为不应该允许在不发表评论的情况下投票......当他们因为尝试而受到抨击时,任何人都打算学习吗?! 话虽如此,重新查看您的答案,我不相信临时问题(抑制在单个脚本中使用的错误消息)的永久解决方案(基本上包装 mysql)是最好的方法。 IMO 将 mysql 包装在脚本中的函数中(在此处使用您的方法就可以了)是一种更好的方法。我的 2c... :)【参考方案11】:
shell> mysql_config_editor set --login-path=local
     --host=localhost --user=localuser --password
Enter password: enter password "localpass" here
shell> mysql_config_editor set --login-path=remote
     --host=remote.example.com --user=remoteuser --password
Enter password: enter password "remotepass" here

要查看 mysql_config_editor 写入 .mylogin.cnf 文件的内容,请使用打印命令:

shell> mysql_config_editor print --all
[local]
user = localuser
password = *****
host = localhost
[remote]
user = remoteuser
password = *****
host = remote.example.com

打印命令将每个登录路径显示为一组行,以组标题开头,在方括号中指示登录路径名称,然后是登录路径的选项值。密码值被屏蔽,不显示为明文。

如前面的示例所示,.mylogin.cnf 文件可以包含多个登录路径。通过这种方式,mysql_config_editor 可以轻松设置多个“个性”以连接到不同的 MySQL 服务器。稍后,当您调用客户端程序时,可以使用 --login-path 选项按名称选择其中的任何一个。例如,要连接到本地服务器,请使用以下命令:

shell> mysql --login-path=local

要连接到远程服务器,请使用以下命令:

shell> mysql --login-path=remote

【讨论】:

如果我想以 www-data 用户身份执行 mysqldump 命令怎么办? www-data 没有主目录...如何为 www-data 用户设置 mysql_config_editor?【参考方案12】:

来自https://gist.github.com/nestoru/4f684f206c399894952d

# Let us consider the following typical mysql backup script:
mysqldump --routines --no-data -h $mysqlHost -P $mysqlPort -u $mysqlUser -p$mysqlPassword $database

# It succeeds but stderr will get:
# Warning: Using a password on the command line interface can be insecure.
# You can fix this with the below hack:
credentialsFile=/mysql-credentials.cnf
echo "[client]" > $credentialsFile
echo "user=$mysqlUser" >> $credentialsFile
echo "password=$mysqlPassword" >> $credentialsFile
echo "host=$mysqlHost" >> $credentialsFile
mysqldump --defaults-extra-file=$credentialsFile --routines --no-data $database

# This should not be IMO an error. It is just a 'considered best practice'
# Read more from http://thinkinginsoftware.blogspot.com/2015/10/solution-for-mysql-warning-using.html

【讨论】:

【参考方案13】:

您也可以将标准错误 STDERR 输出重定向到 /dev/null

那就这样吧:

mysql -u $user -p$password -e "statement" 2&gt; /dev/null

【讨论】:

我会避免这种方法,因为它会使合法错误更难被发现【参考方案14】:

另一种方法是使用 sshpass 调用 mysql,例如:

sshpass -p topsecret mysql -u root -p username -e 'statement'

【讨论】:

似乎可行,但您必须从命令行中删除“用户名”,不是吗?【参考方案15】:

这是 Docker 在脚本 /bin/sh 中的解决方案:

docker exec [MYSQL_CONTAINER_NAME] sh -c 'exec echo "[client]" > /root/mysql-credentials.cnf'

docker exec [MYSQL_CONTAINER_NAME] sh -c 'exec echo "user=root" >> /root/mysql-credentials.cnf'

docker exec [MYSQL_CONTAINER_NAME] sh -c 'exec echo "password=$MYSQL_ROOT_PASSWORD" >> /root/mysql-credentials.cnf'

docker exec [MYSQL_CONTAINER_NAME] sh -c 'exec mysqldump --defaults-extra-file=/root/mysql-credentials.cnf --all-databases'

替换 [MYSQL_CONTAINER_NAME] 并确保在您的容器中设置了环境变量 MYSQL_ROOT_PASSWORD。

希望它能帮助你,就像它可以帮助我一样!

【讨论】:

不错。我只使用了一个带有返回的调用,例如docker exec [MYSQL_CONTAINER_NAME] sh -c 'exec echo "[client] [RETURN HERE] password=pa55" &gt; /root/defaults' 使用 --defaults-file 它也已经获得了 root 权限。【参考方案16】:

定义助手:

remove-warning () 
    grep -v 'mysql: [Warning] Using a password on the command line interface can be insecure.'

使用它:

mysql -u $user -p$password -e "statement" 2>&1 | remove-warning

塔查安!您的代码干净且易于阅读

(用 bash 测试)

【讨论】:

【参考方案17】:

就个人而言,我使用脚本包装器来捕获该错误。这是代码示例:

#!/bin/bash

#echo $@ | cat >> /home/mysqldump.log 2>/dev/null
ERR_FILE=/tmp/tmp_mdump.err

# Execute dumper
/usr/bin/mysqldump $@ 2>$ERR_FILE

# Determine error and remove tmp file
ERROR=`cat $ERR_FILE`
rm $ERR_FILE

# Handle an error
if [ "" != "$ERROR" ]; then

        # Error occured
        if [ "Warning: Using a password on the command line interface can be insecure." != "$ERROR" ]; then
                echo $ERROR >&2
                exit 1
        fi
fi

【讨论】:

【参考方案18】:

对于 PowerShell(pwsh,而不是 bash),这是一个非常简单的解决方案......我的第一次尝试是将对 mysql 的调用包装在 try/catch 函数中,但由于一些strange behavior in PowerShell error handling,这是不可行的。

解决方案是覆盖 $ErrorActionPreference 足够长的时间以组合和捕获 STDERRSTDOUT 并解析单词 ERROR 并根据需要重新抛出。我们无法在 "^mysql.*Warning.*password" 上捕获和释放的原因是 PowerShell 将错误作为一个流处理并引发,因此您必须全部捕获才能过滤和重新抛出。 :/

Function CallMySQL() 
    # Cache the error action preference
    $_temp = $ErrorActionPreference
    $ErrorActionPreference = "Continue"

    # Capture all output from mysql
    $output = (&mysql --user=foo --password=bar 2>&1)

    # Restore the error action preference
    $ErrorActionPreference = $_temp

    if ($output -match "ERROR") 
        throw $output
     elseif($output) 
        "   Swallowing $output"
     else 
        "   No output"
    

注意: PowerShell 可用于 Unix,因此该解决方案是跨平台的。它可以通过一些小的语法修改来适应bash

警告: 有许多边缘情况不起作用,例如非英语错误消息或在输出中的任何位置返回单词 ERROR 的语句,但这已经足够了吞下对mysql 的基本调用的警告,而不会炸毁整个脚本。希望其他人觉得这很有用。

如果mysql 简单地添加一个选项来禁止此警告,那就太好了。

【讨论】:

【参考方案19】:

如果您碰巧使用 Rundeck 来安排您的任务,或者您要求 mylogin.cnf 文件的任何其他平台,我已经成功地使用以下 shell 代码为该文件提供了一个新位置,然后再继续进行 sql 调用:

if test -f "$CUSTOM_MY_LOGINS_FILE_PATH"; then
   chmod 600 $CUSTOM_MY_LOGINS_FILE_PATH
   export MYSQL_TEST_LOGIN_FILE="$CUSTOM_MY_LOGINS_FILE_PATH"
fi

...

result=$(mysql --login-path=production -NBA -D $schema -e "$query")

其中MYSQL_TEST_LOGIN_FILE 是一个环境变量,可以设置为与默认路径不同的文件路径。

如果您在分叉进程中运行并且无法将文件移动或复制到$HOME 目录,这将特别有用。

See documentation here.

【讨论】:

【参考方案20】:

@david-g 的答案非常好,但至少 BSD grep 会将 [Warning] 解释为单个字符的正则表达式表示(匹配单词 Warning 中的任何一个字符) .

对于我的脚本,我使用相同的方法,包装在一个函数中(为了简单起见,只匹配字符串的一部分):

mysql() 
  (
    (
      (
        (
          (
            /usr/local/bin/mysql "$@"
          ) 1>&9
        ) 2>&1
      ) | grep -v 'Using a password on the command line interface can be insecure.'
    ) 1>&2
  ) 9>&1


out=$(mysql ...... 2>&1)
rc=$?

上面会用一组选项调用mysql,将过滤后的stderr重定向到stdout,并捕获$opt中的组合输出和$rc中的退出代码。

【讨论】:

【参考方案21】:

我遇到的问题是在 bash 脚本中使用条件输出。

这并不优雅,但在 docker 环境中,这应该无关紧要。 基本上所有这些都是忽略不在最后一行的输出。 您可以使用 awk 执行类似操作,并更改为返回除第一行以外的所有内容等。

这只返回最后一行

mysql -u db_user -pInsecurePassword my_database ... | sed -e '$!d'

它不会抑制错误,但会确保您可以在 bash 脚本中使用查询的输出。

【讨论】:

【参考方案22】:

最好的解决办法是使用别名:

alias [yourapp]-mysql="mysql -u root -psomepassword -P3306 -h 127.0.0.1"

例如,把它放在你的脚本中:

alias drupal-mysql="mysql -u root -psomepassword -P3306 -h 127.0.0.1"

然后在您的脚本中加载数据库:

drupal-mysql database_name < database_dump.sql

运行语句:

drupal-mysql -e "EXEC SOMESTATEMENT;"

【讨论】:

然后取消别名: 仅供参考,如果您在脚本内的函数中调用 mysql,则不能使用别名。 别名不会隐藏警告,现在我可以在您的控制台中输入“别名”并查看您的密码...【参考方案23】:

另一种解决方案(例如,来自脚本):

 sed -i'' -e "s/password=.*\$/password=$pass/g" ~/.my.cnf
 mysql -h $host -u $user $db_name -e "$sql_cmd"

-i'' 选项用于与 Mac OS X 兼容。标准 UNIX 操作系统可以直接使用 -i

【讨论】:

密码仍然在 'sed' 的命令行上,所以在进程列表中仍然可见,即使只是短暂的。【参考方案24】:

它对我有用- 刚刚在$(mysql_command) 之后添加了2&gt; null,它只会抑制错误和警告消息。

【讨论】:

这也意味着当出现其他问题时您不会收到报告! 另外,您可能想要2&gt;/dev/null。使用2&gt; null 只会将输出放入当前目录中名为“null”的文件中。【参考方案25】:

您可以使用 /dev/null 执行 mySQL 并抑制警告和错误消息 例如:

# if you run just a SQL-command
mysql -u $USERNAME -p$PASSWORD -h $HOST $DATABASE -e "$STATEMENT" &> /dev/null

# Or you can run SQL-script as a file
mysql -u $USERNAME -p$PASSWORD -h $HOST $DATABASE < $FILEPATH &> /dev/null

地点:

$USERNAME - existing mysql user

$PASSWORD - password

$HOST     - ip or hostname, for example 'localhost'

$DATABASE - name of database

$STATEMENT- SQL command

$FILEPATH - Path to the SQL-script

享受吧!

【讨论】:

请注意,它会抑制所有错误消息,而不仅仅是那些密码警告。【参考方案26】:

最简单的方法:

mysql -u root -p YOUR_DATABASE

输入这个,你需要输入你的密码。

注意:是的,没有分号。

【讨论】:

如果您在控制台,这将有效。如果我每天运行一次转储,我不想在控制台...

以上是关于在终端中使用 mysql 来抑制警告消息,但密码是用 bash 脚本编写的的主要内容,如果未能解决你的问题,请参考以下文章

抑制 R 中的 C 警告消息

在 Perl 中抑制无法定位模块警告

导入模块时抑制 scapy 警告消息

调用 Set-ExecutionPolicy 时如何抑制来自脚本的警告消息

抑制 PDO 警告

C#:有啥方法可以抑制编译器错误,类似于抑制警告消息?