Redirecionamento para um nome de arquivo globbed falha

2

Eu uso o Bash 4.3.48 (1) no Ubuntu 16.04 (xenial) com uma pilha LEMP.

Eu tento criar um arquivo de sobreposições de php.ini de uma maneira agnóstica da versão com printf .

1) A operação agnóstica da versão falha:

printf "[PHP]\n post_max_size = 200M\n upload_max_filesize = 200M\n cgi.fix_pathinfo = 0" > /etc/php/*/fpm/zz_overrides.ini

O seguinte erro é fornecido:

bash: /etc/php/*/zz_overrides.ini: No such file or directory

2) A operação gnóstica da versão é bem-sucedida:

printf "[PHP]\n post_max_size = 200M\n upload_max_filesize = 200M\n cgi.fix_pathinfo = 0" > /etc/php/7.0/fpm/zz_overrides.ini

Como você pode ver, ambos são basicamente idênticos, além de * vs 7.0 .

  • Eu não encontrei uma pista sobre esse problema (regex?) em man printf .
  • Eu procurei no Google e não encontrei nada sobre "permitir regex no printf".

Por que a operação agnóstica da versão falha e há algum desvio?

Edit: Se possível, é mais importante para mim usar uma operação de uma linha.

    
por pntshere 27.01.2018 / 19:54

1 resposta

4

O comportamento de uma correspondência de padrão em um redirecionamento parece diferir entre os shells. Dentre os do meu sistema, dash e ksh93 não expandem o padrão, então você obtém um nome de arquivo com um literal * . O Bash expande (1) , mas apenas se o padrão corresponder a um arquivo. Ele reclama se houver mais nomes de arquivos correspondentes. Zsh funciona como se você tivesse dado vários redirecionamentos, ele redireciona a saída para todos os arquivos correspondentes.

(1), exceto quando não é interativo e no modo POSIX

Se você deseja que a saída vá para todos os arquivos correspondentes, use tee :

echo ... | tee /path/*/file > /dev/null

Se você quiser ir para apenas um arquivo, o problema é decidir qual deles usar. Se você quiser verificar se há apenas um arquivo que corresponda ao padrão, expanda a lista inteira e conte-os.

No bash / ksh:

names=(/path/*/file)
if [ "${#names[@]}" -gt 1 ] ; then
    echo "there's more than one"
else
    echo "there's only one: ${names[0]}"
fi

No shell padrão, set dos parâmetros posicionais e use $# para a contagem.

É claro que, se o padrão não corresponder a nenhum arquivo, ele ficará como está e, como o glob está no meio, o resultado aponta para um diretório inexistente. É o mesmo que tentar criar /path/to/file , antes que /path/to exista, é só aqui que temos /path/* , literalmente, com o asterisco.

Para lidar com isso, você teria que expandir o (s) nome (s) do diretório sem o nome do arquivo e, em seguida, acrescentar o nome do arquivo a todos os diretórios. Isso é um pouco feio ...

dirs=(/path/*)
files=( "${dirs[@]/%/\/file}" )

e depois podemos usar esse array:

echo ... | tee "${files[@]}" > /dev/null

Ou podemos pegar o caminho mais fácil e percorrer o padrão do nome do arquivo. É um pouco insatisfatório no caso mais geral, já que requer a execução do comando principal uma vez para cada arquivo de saída ou o uso de um arquivo temporário para manter a saída.

for dir in /path/* ; do
    echo ... > "$dir/file"
done 
    
por 27.01.2018 / 20:35