`read` 命令在 Makefile 中不起作用

`read` 命令在 Makefile 中不起作用

我有一个 make 脚本来执行 3 项任务:

  1. 导入 MySQL 数据库
  2. 移动配置文件
  3. 配置配置文件

对于这些任务,脚本需要 3 个输入:

  1. MySQL主机
  2. MySQL 用户名
  3. 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

如您所见,它输出sernameassword。我这辈子都无法解决这个问题!

虽然这篇文章的目的是解决该read错误,但我将不胜感激任何建议、错误报告或建议。我可能会给他们悬赏。

感谢您的帮助!

答案1

make在那里混合了 shell 和变量。两者make和 shell 都使用$它们的变量。

在 Makefile 中,变量是$(var)or$v表示单字母变量,and $varor${var}在 shell 中。

但如果你写$var在Makefile中,make就会将其理解为$(v)ar.如果要将文字传递$给 shell,则需要将其输入为$$、 in$$var或 ,$${var}以便它成为shell 的$var或。${var}

另外,make运行sh,不bash解释该代码(编辑,抱歉错过了SHELL := /bin/bash上面的内容,请注意,许多系统根本没有bashin /bin(如果有)bash并且:=是 GNU 特定的),因此您需要sh在那里使用语法。echo -nread -szsh/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/。
  • 你的;trueaftermv并没有像你想象的那样做。它不会取消该errexit选项的效果(对于make使用 调用 shell 的实现-e)。你需要的是||true。在这里,我们不使用errexit而是在需要的地方自己进行错误处理|| exit
  • 您需要转义反斜杠&s:pattern:repl:分隔符(此处:),否则它将不起作用(并且可能会产生令人讨厌的副作用)。
  • 你不能这样做sed ... < file > file,因为在开始file之前就会被截断。sed一些sed实现支持-ior-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

相关内容