Como passar o valor de uma variável para o stdin de um comando?

105

Estou escrevendo um script de shell que deve ser um tanto seguro, ou seja, não passa dados seguros por meio de parâmetros de comandos e, de preferência, não usa arquivos temporários. Como posso passar uma variável para o stdin de um comando? Ou, se não for possível, como usar corretamente os arquivos temporários para tal?

Jonathan Leffler
fonte
Um invasor pode mudar $PATH? Para que catpossa ser substituído por/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Respostas:

74

Algo tão simples como:

echo "$blah" | my_cmd
Oliver Charlesworth
fonte
7
Cuidado com os espaços em sua variável: echo "$blah"é melhor.
Jonathan Leffler
13
Vamos ver como você lida com blah=-n, blah=-e... use em seu printflugar. unix.stackexchange.com/questions/65803/…
Camilo Martin
1
parece que seria frágil e sujeito a erros, não?
ThorSummoner
20
6 anos depois, se eu pudesse excluir esta resposta, eu o faria (mas não posso porque foi aceita ...)
Oliver Charlesworth
1
@OliverCharlesworth, por que não editá-lo para usar printf '%s\n' "$blah"? Com essa única mudança (evitando as muitas armadilhas na echoespecificação de, cuja excelente resposta de Stephane entra em detalhes em Por que é printfmelhor que echo? Em Unix e Linux , ou que a seção USO DE APLICATIVO da echoespecificação aborda mais brevemente) é um perfeito resposta razoável.
Charles Duffy
192

Passar um valor para stdinem bash é tão simples quanto:

your-command <<< "$your_variable"

Sempre certifique-se de colocar aspas nas expressões variáveis!

Tenha cuidado, pois isso provavelmente funcionará apenas bashe não funcionará em sh.

Martin
fonte
39
Não <<<há garantia de disponibilidade de herestrings , elas não estão na base POSIX, até onde eu sei. Eles funcionarão conforme o esperado, contanto que você apenas os execute bash. Eu apenas mencionei isso, porque eles disseram 'shell' e não especificamente 'bash'. Embora ele tenha marcado a pergunta com bash... então esta ainda é obviamente uma resposta aceitável.
JM Becker
Embora esse possa ser o caso, as informações confidenciais provavelmente vazarão mais facilmente se o echométodo for usado devido à criação de um tubo. O comando herestring será processado inteiramente por bash, embora nesta situação (conforme observado), é melhor saber que ele funcionará em seu bash, caso contrário, qualquer mensagem de erro produzida como resultado de não suportar herestrings também vazaria essa informação.
Steven Lu
@StevenLu Espere, echoé um built-in. Não tem tubo aí, certo? É Unix, mas não pode ser tanto Unix .
Camilo Martin
4
@StevenLu printf '%s\n' "$var"produz os mesmos resultados que echo "$var"mas não quebrará se, por exemplo ,, var=-ene nem mesmo requer bash.
Camilo Martin
1
Observe também que uma nova linha é acrescentada à string para as strings here.
PDR
19

Observe que as echo "$var" | commandoperações ' significam que a entrada padrão é limitada à (s) linha (s) ecoada (s). Se você também deseja que o terminal seja conectado, você precisa ser mais sofisticado:

{ echo "$var"; cat - ; } | command

( echo "$var"; cat -   ) | command

Isso significa que a (s) primeira (s) linha (s) será o conteúdo de, $varmas o resto virá da catleitura de sua entrada padrão. Se o comando não fizer nada muito sofisticado (tente ativar a edição de linha de comando ou executar como vimfaz), então tudo bem. Caso contrário, você precisa ser realmente sofisticado - eu acho expectou um de seus derivados provavelmente será apropriado.

As notações da linha de comando são praticamente idênticas - mas o segundo ponto-e-vírgula é necessário com as chaves, embora não seja entre parênteses.

Jonathan Leffler
fonte
( echo "$LIST"; cat - ) | sed 1qisso funciona para mim, mas preciso pressionar ctrl d quando executo este script?
Gert Cuykens
Sim; O cat -continua a ler do teclado até EOF ou interromper, então você precisa dizer EOF digitando control-D.
Jonathan Leffler
existe uma maneira de contornar o EOF? sed precisa de gato
Gert Cuykens
Você não precisa usar catnada se não quiser entrada de terminal, como na primeira linha. Ou você pode usar catpara listar um arquivo. Ou ... Se quiser que o comando leia a entrada do terminal, você deve avisá-lo quando chegar ao fim da entrada. Ou você pode usar ( echo "$var"; sed /quit/q - ) | command; isso continua até que você digite uma linha contendo 'quit'. Você pode ser infinitamente criativo ao lidar com isso. Cuidado com a velha lenda urbana de um programa que parou de funcionar quando os usuários começaram a trabalhar com o Equador. Eles digitavam o nome da capital, Quito, e o programa era encerrado.
Jonathan Leffler,
Ta bem se você diz. Mas por que não apenas echo "$LIST" | sed 1q | ...? Tudo depende do que você faz. A <<EOFnotação deve exigir um EOF no início de uma linha por conta própria em algum lugar abaixo do arquivo. Eu não usaria, eu acho - mas ainda não tenho certeza do que você está tentando alcançar. Um simples echo "$LIST" | commandou echo $LIST | commandprovavelmente será suficiente na prática (e é importante que você saiba o significado da diferença entre os dois).
Jonathan Leffler,
16

Gostei da resposta do Martin, mas tem alguns problemas dependendo do que está na variável. este

your-command <<< """$your_variable"""

é melhor se sua variável contém "ou!

Robert Jacobs
fonte
4
Mas por que? Além disso, não consigo reproduzir nenhum problema: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"funciona bem para mim. O que exatamente os três "fazem? AFAIK você está apenas prefixando e acrescentando uma string vazia.
phk
Experimente algo real. Como se seu comando fosse ssh somehost. e sua variável é um script de shell.
Robert Jacobs
16
(cat <<END
$passwd
END
) | command

A catnão é realmente necessário, mas ajuda a estruturar o código melhor e permite que você use mais comandos entre parênteses como entrada para seu comando.

PoltoS
fonte
Mas este método permite que você passe várias linhas para o seu comando e também permite espaços no$passwd
PoltoS
4
Esta é a melhor resposta até agora que não vaza conteúdos variáveis ​​para o pipe ou process snooping.
user2688272
8

De acordo com a resposta de Martin, há um recurso bash chamado Here Strings (que é uma variante do recurso Here Documents mais amplamente suportado ).

http://www.gnu.org/software/bash/manual/bashref.html#Here-Strings

3.6.7 Here Strings

Uma variante deste documento, o formato é:

<<< word

A palavra é expandida e fornecida ao comando em sua entrada padrão.

Observe que Here Strings parece ser apenas bash, então, para melhor portabilidade, você provavelmente ficaria melhor com o recurso Here Documents original, de acordo com a resposta de PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

Ou uma variante mais simples do anterior:

(cmd <<EOF
$variable
EOF
)

Você pode omitir (e ), a menos que queira redirecioná-lo para outros comandos.

cnst
fonte
5

Essa forma robusta e portátil já apareceu em comentários. Deve ser uma resposta independente.

printf '%s' "$var" | my_cmd

ou

printf '%s\n' "$var" | my_cmd

Notas:

  • É melhor do que echo, as razões estão aqui: Por que é printfmelhor do que echo?
  • printf "$var"está errado. O primeiro argumento é o formato em que várias sequências semelhantes %sou \nsão interpretadas . Para passar a variável certa, ela não deve ser interpretada como formato.
  • Normalmente, as variáveis ​​não contêm novas linhas finais. O comando anterior (com %s) passa a variável como está. No entanto, as ferramentas que trabalham com texto podem ignorar ou reclamar de uma linha incompleta (consulte Por que os arquivos de texto devem terminar com uma nova linha? ). Portanto, você pode querer o último comando (com %s\n) que anexa um caractere de nova linha ao conteúdo da variável. Fatos não óbvios:

    • Aqui, string em Bash ( <<<"$var" my_cmd) adiciona uma nova linha.
    • Qualquer método que acrescenta uma nova linha resulta em um stdin não vazio de my_cmd, mesmo se a variável estiver vazia ou indefinida.
Kamil Maciorowski
fonte
4

Experimente isto:

echo "$variable" | command
incrédulo
fonte
mas a variável de conteúdo $ não apareceria, por exemplo, na saída de ps -uquando o echo está em execução?
2
não, não vai. echoé embutido, então não há processo para mostrar em ps
unbeli
2
Cuidado com os espaços em sua variável: echo "$variable"é melhor.
Jonathan Leffler
isso mesmo, o bash comerá espaços duplos, os shells mais inteligentes não
incrédulo
-1

Apenas faça:

printf "$my_var" | my_cmd

Se var não contiver espaços, as aspas podem ser omitidas.
Se estiver usando o bash, você também pode fazer:

echo -n "$my_var" | my_cmd

Evite usar echo sem -n porque ele canalizará a variável com uma quebra de linha adicionada no final.

Clox
fonte
1
não funciona se $ my_var for %s, printf não imprimirá nada. my_var="%s"; printf "$my_var"; - talvez tente printf "%s" "$my_var" | my_cmd ?
hanshenrik