2

我有一个CakePHP database.php 配置文件,我想利用它来打开命令行mysql客户端实例。

目标是能够像./db-login.sh从项目根目录一样运行 shell 脚本,并让它从 PHP 配置文件中提取数据库详细信息(主机、端口、数据库名称、用户名、密码)并将它们作为参数传递给mysql命令行. 这是为了避免每次都输入详细信息。

我知道可以创建一个硬编码值的 shell 别名:我想要一个可以包含在我的任何Cake 项目中的可移植脚本。我还希望将数据库凭据放入 bash 变量的任务与启动mysql客户端分开。mysqldump这将打开在其他 shell 脚本(例如备份脚本)中轻松重用数据库凭据的能力。

这是我到目前为止所拥有的:

数据库.php

考虑到这个问题的目的,这个文件是不可变的。它必须与你看到的完全一样。

<?php
class DATABASE_CONFIG {
    public $default = array(
        'host'        => 'localhost',
        'login'       => 'cakephpuser',
        'password'    => 'c4k3 roxx!',
        'database'    => 'my_cakephp_project',
    );
}

db-cred.sh

充当将 PHP 变量转换为 bash(友好)变量的中间件。

#!/usr/bin/env php
<?php
include 'database.php';
$db = new DATABASE_CONFIG(); 

// Selectively wrap quotes. Has no real effect(?)
$p = (!empty($db->default['password']) ? "\"$db->default['password']\"" : '');

echo <<<EOD
DB_HOST="{$db->default['host']}"
DB_NAME="{$db->default['database']}"
DB_USER="{$db->default['login']}"
DB_PASS={$p}
EOD;

db-login.sh

#!/usr/bin/env bash
# Uses Cake's database.php file to log into mysql on the 
# command line with the correct arguments.

# Holy yuck, but nothing else works!
eval $( db-cred.sh )

# Try to set an appropriate password clause.
PATTERN=" |'"
if [ -n "$DB_PASS" ]; then
    if [[ $DB_PASS =~ $PATTERN ]]; then
        PASS_CLAUSE=" -p'${DB_PASS}'"
    else
        PASS_CLAUSE=" -p${DB_PASS}"
    fi
else
    PASS_CLAUSE=""
fi

# Get a rough look at what we're about to run.
echo mysql --host=${DB_HOST} --database=${DB_NAME} --user=${DB_USER}${PASS_CLAUSE}

# Call MySQL.
mysql --host=${DB_HOST} --database=${DB_NAME} --user=${DB_USER}${PASS_CLAUSE}

需要帮助的事情:

  • db-login.sh脚本使用 eval 来吸收变量。呸。
  • MySQL 密码在命令行上泄露。这不是一个破坏者,但如果有一种干净/便携的方式来避免它,我愿意接受。
  • mysql 密码中的空格和引号不能正确传递给 bash。
    • 像这样的密码my Pass将导致 DB_PASS 设置为myin db-login.sh
    • db-cred.sh而是在产生中引用字符串"my
  • 同样,我的引用尝试PASS_CLAUSE无效:只有没有空格/引号的密码才能成功登录mysql客户端。
4

2 回答 2

2
  1. db-login.sh 脚本使用 eval 来吸收变量。呸。

    代替eval,您可以使用以下source命令:

    source db-cred.sh
    
  2. MySQL 密码在命令行上泄露。这不是破坏交易,但如果有一种干净/便携的方法可以避免它,我愿意接受。

    您可以在使用后取消设置 MySQL 密码:

    unset DB_PASS
    

    但是,如果您通过命令行提供了 MySQL 的密码,您将始终遇到此问题。您可以删除该-pPassword子句并在 MySQL 提示时强制用户输入密码,但从您的用例来看,这似乎不可行。

  3. mysql 密码中的空格和引号不能正确传递给 bash。

  4. 同样,我引用 PASS_CLAUSE 的尝试无效:只有没有空格/引号的密码才能成功登录 mysql 客户端。

对于 3 和 4,我相信您需要做的就是正确引用您的变量值。

在 PHP 中:

echo <<<EOD
DB_HOST="{$db->default['host']}"
DB_NAME="{$db->default['database']}"
DB_USER="{$db->default['login']}"
DB_PASS="{$p}"
EOD;

请注意 中添加的引号DB_PASS="{$p}"

提醒一下,您还可以在 PHP 中使用addslashes().

于 2013-05-01T15:38:27.323 回答
0

好的,所以@nickb 的建议通过足够的修改使我走上了正确的道路。

解释

如果您尝试通过多个 bash 变量传递 MySQL 密码,则会出现问题。这是一个精简的示例:

#!/bin/bash
set -x
DB_PASS="pass with space and 'quote"
PASS_CLAUSE=" -p'$DB_PASS'"

# Doesn't work.
mysql $PASS_CLAUSE

# Works.
mysql -p"$DB_PASS"

请注意,set -x我们可以看到实际正在运行的命令。这就是帮助我识别双重转义的原因。$DB_PASS 中的字符串在保存到 $PASS_CLAUSE 时会重新转义。因为它被传递到 MySQL 时仍然存在一级转义,所以第一个连接失败,但使用原始变量成功。

最终脚本

db-cred.sh

#!/usr/bin/php
<?php
include 'database.php';
$db = new DATABASE_CONFIG(); 
echo <<<EOD
DB_HOST="{$db->default['host']}"
DB_NAME="{$db->default['database']}"
DB_USER="{$db->default['login']}"
DB_PASS="{$db->default['password']}"
EOD;

db-login.sh

#!/bin/bash

# Holy yuck, but nothing else works!
eval $( db-cred.sh )

CMD="mysql --host=${DB_HOST} --database=${DB_NAME} --user=${DB_USER}"
PATTERN=" |'"

if [[ ${DB_PASS} =~ ${PATTERN} ]]; then
    ${CMD} -p"${DB_PASS}"
elif [ -n "${DB_PASS}" ]; then
    ${CMD} -p${DB_PASS}
else
    ${CMD}
fi

最后的想法

  • 我没有专门测试过,但是包含双引号的密码可能仍然存在问题。
  • 这仍然可以通过某种方式消除对eval. @Dave 关于使用 CSV 作为中间选项的想法具有潜力,但在我的测试中,它通过添加另一个级别的转义/取消转义以将变量干净地放入 bash 中,从而使事情变得更加复杂。
  • MySQL 密码仍然在命令行上使用。编写一个 mysql 配置文件,使用它--defaults-extra-file=filename然后删除它可能是一种解决方法。
  • db-login.sh现在代表了一种使用数据库凭据执行命令行的模式,并且可以很容易地修改/扩展为其他工具,例如mysqldump.
于 2013-05-01T18:22:13.450 回答