Usando | o caractere de pipe de uma variável $ faz com que seja tratado apenas como outro argumento no bash; como escapar?


8

Eu tenho um script bash como este

export pipedargument="| sort -n"
ls $pipedargument

Mas dá o erro

ls: |: No such file or directory
ls: sort: No such file or directory

Parece tratar o conteúdo de "| sort -n"apenas um argumento passado para ls.

Como posso escapar para que seja tratado como um comando canalizado regular?

Estou tentando definir condicionalmente o $pipedargument. Eu acho que eu poderia executar condicionalmente versões diferentes do comando, mas ainda me perguntando se há uma maneira de fazer isso funcionar como acima?

Respostas:


9

Você está certo que não pode usar |dessa maneira. O motivo é que o shell já procurou por pipelines e os separou em comandos antes de fazer a substituição da variável. Portanto, |é tratado como apenas mais um personagem.

Uma solução possível é colocar o caractere de pipe literalmente:

$ cmd="sort -n"
$ ls | $cmd

No caso de você não desejar um pipeline, você pode usar catcomo "nop" ou espaço reservado:

$ cmd=cat
$ ls | $cmd

Este método evita as sutilezas de eval . Veja também aqui .

Uma abordagem melhor: matrizes

Uma abordagem mais sofisticada usaria bashmatrizes no lugar de strings simples:

$ cmd=(sort -n)
$ ls | "${cmd[@]}"

A vantagem das matrizes se torna importante assim que você precisar que o comando cmdcontenha argumentos entre aspas.


Você poderia esclarecer por que as matrizes são necessárias no caso de argumentos citados? Eu me deparei com isso, mas não entendo direito por que não funciona.
Ansieux 16/03/19

@anxieux Uma resposta curta é que uma citação, quando dentro de uma string de shell, perde todo o seu poder sintático para agrupar palavras e é tratada como qualquer outro caractere. Para uma discussão mais longa e excelente sobre esse problema, consulte: Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham! .
John1024

5

Você pode evalatualizar o comando:

eval "ls $pipedargument"

ou melhor ainda, definir funções como:

sorted() { "$@" | sort -n; }

e depois chame-o com os argumentos desejados:

sorted ls /tmp

Outra opção seria definir um alias:alias ls='ls | sort -n'
thiagowfx 5/15/15

0

Eu usaria uma função para isso. Algo como:

### usage pipedargument cmd args ###

pipedargument()
{
    sort -n <<< "$( "$@" )"
}

$ pipedargument /sbin/ifconfig eth0
      RX bytes:5904986765 (5.4 GiB)  TX bytes:714370767 (681.2 MiB)
      RX packets:5981427 errors:0 dropped:0 overruns:0 frame:0
      TX packets:4403989 errors:0 dropped:0 overruns:0 carrier:0
      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
      collisions:0 txqueuelen:1000 
      inet addr:XXX.XXX.X.XX  Bcast:XXX.XXX.X.XXX  Mask:255.255.255.0
      inet6 addr: xx00::0x0x:00xx:xx0:000/00 Scope:Link
eth0      Link encap:Ethernet  HWaddr 0x:0x:00:x0:00:00
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.