我有一个 make 脚本来执行 3 项任务:
- 导入 MySQL 数据库
- 移动配置文件
- 配置配置文件
对于这些任务,脚本需要 3 个输入:
- MySQL主机
- MySQL 用户名
- MySQL 密码
由于某种原因,每当我read
输入时,它都会保存到正确的变量,但内容是我保存到的变量名称,没有第一个字母。我似乎无法找出它为什么这样做。
这是Makefile
:
SHELL := /bin/bash
default:
@echo "Welcome!";\
echo -n "Please enter the MySQL host (default: localhost):";\
read host;\
host=${host:-localhost};\
echo -n "Please enter the MySQL username:";\
read username;\
echo -n "Please enter the MySQL password:";\
read -s password;\
mv includes/config.php.example includes/config.php 2>/dev/null;true;\
sed 's/"USER", ""/"USER", "$(username)"/g' includes/config.php > includes/config.php;\
sed 's/"PASSWORD", ""/"PASSWORD", "$(password)"/g' includes/config.php > includes/conf$
echo $username;\
echo $password;\
mysql -u "$username" -p"$password" codeday-team < ./codeday-team.sql;\
echo "Configuration complete. For further configuration options, check the config file$
exit 0;
输出是:
Welcome!
Please enter the MySQL host (default: localhost):
Please enter the MySQL username:<snip>
Please enter the MySQL password:sername
assword
ERROR 1045 (28000): Access denied for user 'sername'@'localhost' (using password: YES)
Configuration complete. For further configuration options, check the config file includes/config.php
如您所见,它输出sername
和assword
。我这辈子都无法解决这个问题!
虽然这篇文章的目的是解决该read
错误,但我将不胜感激任何建议、错误报告或建议。我可能会给他们悬赏。
感谢您的帮助!
答案1
你make
在那里混合了 shell 和变量。两者make
和 shell 都使用$
它们的变量。
在 Makefile 中,变量是$(var)
or$v
表示单字母变量,and $var
or${var}
在 shell 中。
但如果你写$var
在Makefile中,make
就会将其理解为$(v)ar
.如果要将文字传递$
给 shell,则需要将其输入为$$
、 in$$var
或 ,$${var}
以便它成为shell 的$var
或。${var}
另外,make
运行sh
,不bash
解释该代码(编辑,抱歉错过了SHELL := /bin/bash
上面的内容,请注意,许多系统根本没有bash
in /bin
(如果有)bash
并且:=
是 GNU 特定的),因此您需要sh
在那里使用语法。echo -n
,read -s
是zsh
/bash
语法,不是sh
语法。
最好是添加一个zsh
/bash
脚本来执行此操作(并添加对 的构建依赖项bash
)。就像是:
#! /usr/bin/env bash
printf 'Welcome\nPlease enter the MySQL host (default: localhost): '
read host || exit
host=${host:-localhost}
printf "Please enter the MySQL username: "
read username || exit
printf "Please enter the MySQL password:"
IFS= read -rs password || exit
printf '\n'
mv includes/config.php.example includes/config.php 2>/dev/null
repl=${password//\\/\\\\}
repl=${repl//&/\\&}
repl=${repl//:/\\:}
{ rm includes/config.php &&
sed 's/"USER", ""/"USER", "'"$username"'"/g
s:"PASSWORD", "":"PASSWORD", "'"$repl"'"/g' > includes/config.php
} < includes/config.php || exit
mysql -u "$username" -p"$password" codeday-team < ./codeday-team.sql || exit
echo "Configuration complete. For further configuration options, check the config file"
它解决了更多问题:
-r
如果您想允许用户在密码中使用反斜杠,则在读取密码时需要这样做。IFS=
如果您想允许用户拥有以空格开头或结尾的密码,则需要- 这同样适用于用户名,但这里我们假设用户不会对用户名使用任何愚蠢的东西,并且空白剥离和反斜杠处理可以被视为/feature/。
- 你的
;true
aftermv
并没有像你想象的那样做。它不会取消该errexit
选项的效果(对于make
使用 调用 shell 的实现-e
)。你需要的是||true
。在这里,我们不使用errexit
而是在需要的地方自己进行错误处理|| exit
。 - 您需要转义反斜杠
&
和s:pattern:repl:
分隔符(此处:
),否则它将不起作用(并且可能会产生令人讨厌的副作用)。 你不能这样做
sed ... < file > file
,因为在开始file
之前就会被截断。sed
一些sed
实现支持-i
or-i ''
选项。或者你可以使用perl -pi
.在这里,我们所做的相当于perl -pi
手动操作(在打开输入文件进行读取后删除并重新创建输入文件),但不处理文件的元数据。在这里,最好使用例子一个作为输入,最后一个作为输出。
它仍然没有解决更多问题:
- 新的权限
config.php
是使用从当前权限派生的权限创建的umask
,这可能是世界可读并由运行的用户拥有make
。如果目录没有受到其他限制,因为该文件包含敏感信息,您可能需要调整umask
和/或更改所有权。includes
- 如果密码包含
"
字符,则可能会损坏(这次是php
)。您想要禁止这些(通过返回错误)或以该php
文件的正确语法为它们添加另一层转义。您可能会遇到类似的反斜杠问题,并且您可能还想排除非 ASCII 字符或控制字符。 在 的命令行上传递密码
mysql
通常是一个坏主意,因为这意味着它会显示在 的输出中ps -f
。最好使用:mysql --defaults-file=<( printf '[client]\nuser=%s\npassword="%s"\n' "$username" "$password") ...
printf
由于是内置的,因此不会出现在ps
输出中。$host
未使用该变量。- 像这样提示用户意味着您的脚本无法轻松实现自动化。您可以从参数中获取输入,或者使用环境变量(对于密码更好)。
答案2
您没有使用 Makefile 的任何功能,您是否尝试在单个 shell 中编写这些行?
您使用的 Makefile 中的下一个
echo $username;\
echo $password;\
make 将解析$username
为$u
(空变量)+ sername
(字符串)
如果您想保留在 Makefile 中,请尝试替换$username
为。$$username