'read' o comando não funciona em um Makefile

5

Eu tenho um script make para executar 3 tarefas:

  1. Importe um banco de dados MySQL
  2. Mover um arquivo de configuração
  3. Configure o arquivo de configuração

Para estas tarefas, o script requer 3 entradas:

  1. Host MySQL
  2. Nome de usuário do MySQL
  3. Senha do MySQL

Por alguma razão, sempre que eu read uma entrada, ele salva na variável correta, mas o conteúdo é o nome da variável que eu salvei sem a primeira letra. Eu não consigo descobrir por que isso acontece.

Aqui está o 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;

A saída é:

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

Como você pode ver, o resultado é sername e assword . Para a vida de mim eu não posso consertar isso!

Embora o objetivo deste post seja resolver o erro read , agradeço qualquer conselho, relatório de bugs ou sugestões. Eu posso conceder uma recompensa por eles.

Obrigado por ajudar!

    
por Arin 11.11.2016 / 08:30

2 respostas

5

Você está misturando as variáveis shell e make lá. Tanto make quanto o shell usam $ para suas variáveis.

No Makefile, as variáveis são $(var) ou $v para variáveis de uma letra e $var ou ${var} em shells.

Mas se você escrever $var em um Makefile, make entenderá como $(v)ar . Se você quiser passar um literal $ para o shell, precisará inseri-lo como $$ , como em $$var ou $${var} , para que ele se torne $var ou ${var} para o shell.

Além disso, make runs sh , não bash para interpretar esse código ( Editar , desculpe perdeu seu SHELL := /bin/bash acima, observe que muitos sistemas não têm bash em /bin se eles tiverem bash e := forem específicos do GNU), então você precisa usar a sintaxe sh lá. echo -n , read -s são zsh / bash sintaxe, não sh sintaxe.

O melhor aqui seria adicionar um script zsh / bash para fazer isso (e adicionar uma dependência de compilação em bash ). Algo como:

#! /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"

Ele aborda mais alguns problemas:

  • você precisa de -r ao ler a senha se quiser permitir que o usuário use barras invertidas em sua senha.
  • você precisa de IFS= se quiser permitir que o usuário tenha uma senha que inicie ou termine em espaços em branco
  • o mesmo se aplica ao nome de usuário, mas aqui estamos supondo que o usuário não usará nada de bobo para o nome de usuário, e o tratamento em branco de decapagem e contrabarra pode ser visto como /feature/.
  • seu ;true após mv não faz o que você acha que faz. Ele não cancela o efeito da opção errexit (para make implementations que chamam o shell com -e ). Você precisaria de ||true . Aqui, não estamos usando errexit , mas fazendo o erro lidando com || exit onde necessário.
  • Você precisa escapar da barra invertida, & e do s:pattern:repl: separador (aqui : ) ou não funcionará (e pode ter efeitos colaterais desagradáveis).
  • Você não pode fazer sed ... < file > file , pois file seria truncado antes de sed ser iniciado. Algumas implementações sed suportam uma opção -i ou -i '' para isso. Como alternativa, você pode usar perl -pi . Aqui estamos fazendo o equivalente a perl -pi manualmente (excluir e recriar o arquivo de entrada depois de ter sido aberto para leitura), mas sem cuidar dos metadados do arquivo.

    Aqui, seria melhor usar o exemplo como entrada e o final como saída.

Ainda não resolve mais alguns problemas:

  • o novo config.php é criado com permissões derivadas do atual umask , que provavelmente será legível por todos e de propriedade do usuário que estiver executando make . Pode ser necessário adaptar o umask e / ou alterar a propriedade se o includes dir não estiver restrito, pois esse arquivo contém informações confidenciais.
  • se a senha contiver " caracteres, isso provavelmente será interrompido (desta vez para php ). Você pode querer proibi-los (retornando um erro) ou adicionar outra camada de escape para eles na sintaxe correta para esse arquivo php . É provável que você tenha problemas semelhantes com a barra invertida e talvez queira também excluir caracteres não-ascii ou de controle.
  • Passar a senha na linha de comando de mysql geralmente é uma má ideia, já que isso mostra a saída de ps -f . Seria melhor usar:

     mysql --defaults-file=<(
       printf '[client]\nuser=%s\npassword="%s"\n' "$username" "$password") ...
    

    printf sendo interno, ele não seria exibido em ps output.

  • a variável $host não é usada.

  • solicitando ao usuário que isso significa que seu script não pode ser facilmente automatizado. Você poderia pegar a entrada de argumentos ou (melhor para a senha) com variáveis de ambiente.
por 11.11.2016 / 10:41
5

você não está usando nenhum recurso do Makefile, você tentou escrever essas linhas em um único shell?

próximo em um Makefile que você usa

    echo $username;\
    echo $password;\

make irá analisar $username como $u (variável vazia) + sername (string)

tente substituir $username por $$username se você quiser ficar no Makefile.

    
por 11.11.2016 / 09:47