Adicione argumentos ao 'bash -c'

15

Digamos que eu queira executar um comando por meio do Bash assim:

/bin/bash -c "ls -l"

De acordo com a página man do Bash, eu também poderia executá-lo assim:

#               don't process arguments after this one
#               |   pass all unprocessed arguments to command
#               |   |
#               V   V
/bin/bash -c ls -- -l

exceto que não parece funcionar (parece ignorado). Estou fazendo algo errado ou estou interpretando errado a página do manual?

Citações relevantes do homem:

If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.

e

A -- signals the end of options and disables further option processing. Any arguments after the -- are treated as filenames and arguments.

    
por Slartibartfast 14.07.2014 / 19:50

2 respostas

22

Você está interpretando a página man errada. Em primeiro lugar, a parte sobre -- sinalizando o fim das opções é irrelevante para o que você está tentando fazer. O -c substitui o resto da linha de comando daquele ponto em diante, de modo que ele não está mais passando pelo tratamento de opções do bash, significando que o -- seria passado para o comando, não tratado pelo bash como fim de marcador de opções.

O segundo erro é que argumentos extras são designados como parâmetros posicionais para o processo de shell que é lançado, não passados como argumentos para o comando. Então, o que você está tentando fazer pode ser feito como um dos seguintes:

/bin/bash -c 'echo "$0" "$1"' foo bar
/bin/bash -c 'echo "$@"' bash foo bar

No primeiro caso, passando os parâmetros $0 e $1 explicitamente, e no segundo caso, usando "$@" para expandir como normal como "todos os parâmetros posicionais exceto $ 0". Note que nesse caso temos que passar algo para ser usado como $0 também; Eu escolhi "bash" pois é isso que normalmente seria $0 , mas qualquer outra coisa funcionaria.

Como a razão é feita desta maneira, ao invés de apenas passar quaisquer argumentos que você der diretamente para o comando que você lista: note que a documentação diz que "comando s é lido na string", plural. Em outras palavras, esse esquema permite que você faça:

/bin/bash -c 'mkdir "$1"; cd "$1"; touch "$2"' bash dir file

Mas, observe que uma maneira melhor de atingir sua meta original pode ser usar env em vez de bash :

/usr/bin/env -- "ls" "-l"

Se você não precisa de nenhum dos recursos que um shell está fornecendo, não há razão para usá-lo - usar env nesse caso será mais rápido, mais simples e menos digitação. E você não precisa pensar com tanta dificuldade para garantir que ele manipule com segurança nomes de arquivos contendo metacaracteres ou espaços em branco do shell.

    
por 14.07.2014 / 20:20
3

Não sei qual é o seu objetivo, mas se você está simplesmente tentando criar uma máquina Rube Goldberg - “uma geringonça, invenção, dispositivo ou aparelho que é deliberadamente superprojetado ou exagerado para executar uma tarefa muito simples de uma maneira muito complicada” - tente

sh -c 'ls $0' -l

ou

sh -c 'ls $1' supercalifragilisticexpialidocious -l

ou até mesmo

sh -c 'ls -$0' l

Você deve entender como isso funciona na resposta da godlygeek.

    
por 15.07.2014 / 00:07