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

Suppress warning messages using mysql from within Terminal, but password written in bash script

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

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

执行按预期工作,但始终发出警告:

Warning: Using a password on the command line interface can be insecure.

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

  • 禁止警告是否可行?正如我所说的,命令工作正常,但是当我循环运行命令50或100次时,窗口会变得相当混乱。

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

运行EDOCX1[1]没有帮助,只说

--show-warnings
Cause warnings to be shown after each statement if there are any. This option applies to interactive and batch mode.

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

我使用的是OS X 10.9.1 Mavericks,使用的是自制的MySQL5.6。


如果您的MySQL客户机/服务器版本是5.6.x,则避免警告消息的方法是使用MySQL_-config_编辑器工具:

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

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

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

而不是:

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


我使用类似的东西:

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

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

其中config.cnf包含:

1
2
3
4
[client]
user = whatever
password = whatever
host = whatever

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

如果您使用的是基于Debian的发行版,并且以根用户身份运行,那么您可以跳过上面的内容,只需使用/etc/mysql/debian.cnf就可以进入……:

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


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

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

请注意,官方文件对此提出了反对意见。见6.1.2.1最终用户密码安全指南(5.6版MySQL手册):

Storing your password in the MYSQL_PWD environment variable

This method of specifying your MySQL password must be considered extremely insecure and should not be used. Some versions of ps include an option to display the environment of running processes. On some systems, if you set MYSQL_PWD, your password is exposed to any other user who runs ps. Even on systems without such a version of ps, it is unwise to assume that there are no other methods by which users can examine process environments.


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

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

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

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


以下是我如何获得每日mysqldump数据库备份的bash脚本,以更安全地工作。这是克里斯蒂安·波塔伟大答案的扩展。

  • 首先使用mysql_config_editor(与mysql 5.6+一起提供)设置加密的密码文件。假设您的用户名是"db_user"。从shell提示运行:

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

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

    当然,将服务器上的"系统用户名"更改为您的用户名。

  • 从此处更改bash脚本:

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

    对此:

    1
    mysqldump --login-path=local my_database | gzip > db_backup.tar.gz
  • 不再暴露密码。


    最简单的方法是

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


    您还可以在脚本中运行mysql_-config_编辑器,在指定登录路径时传递密码。

    1
    2
    3
    4
    5
    expect -c"
    spawn mysql_config_editor set --login-path=$mySqlUser --host=localhost --user=$mySqlUser --password
    expect -nocase "
    Enter password:" {send "$mySqlPassword
    "; interact}
    "

    这将启动一个Expect会话,该会话可在脚本中用于与提示交互。

    看到这个帖子


    1
    2
    3
    4
    5
    6
    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文件写入的内容,请使用print命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    shell> mysql_config_editor print --all
    [local]
    user = localuser
    password = *****
    host = localhost
    [remote]
    user = remoteuser
    password = *****
    host = remote.example.com

    print命令将每个登录路径显示为一组行,从一个以方括号表示登录路径名的组头开始,后跟登录路径的选项值。密码值被屏蔽,不显示为明文。

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

    1
    shell> mysql --login-path=local

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

    1
    shell> mysql --login-path=remote

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 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

    您还可以将标准错误stderr输出重定向到/dev/null。

    所以就这样做:

    mysql -u $user -p$password -e"statement" 2> /dev/null


    一个简单的解决方案脚本。将此命名为"mysql",并将其放在路径中"/usr/bin"之前。其他命令的明显变体,或者警告文本不同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #!/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


    以下是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。

    希望它能像帮助我一样帮助你!


    另一种选择是使用sshspass调用mysql,例如:

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


    对于PowerShell(pwsh,而不是bash),这是一个非常麻烦的goldberg解决方案…我的第一次尝试是在try/catch函数中包装对mysql的调用,但是由于PowerShell错误处理中的一些奇怪行为,这是不可行的。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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简单地增加一个选项来抑制这一警告,那就太好了。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #!/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


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

    这并不优雅,但在码头环境下,这真的不重要。基本上,所有这些都忽略了不在最后一行的输出。您可以对awk做类似的操作,并更改以返回除第一行以外的所有内容。

    这只返回最后一行

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

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


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

    1
    2
     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


    您可以使用/dev/null执行mysql并禁止警告和错误消息例如:

    1
    2
    3
    4
    5
    # 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

    哪里:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ${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

    享受!


    它对我有用-只是在$(mysql_command)后面加了2> null,只会抑制错误和警告信息。