/ bin / sh: diferença entre variável e comando direto

3

Imagine que eu tenha seguido o script bash:

#/bin/sh
#next line will work correctly, outputting user name and current directory
start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls'

MYVAR="start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls'"
#next line will fail with following error:
#ls': -c: line 0: unexpected EOF while looking for matching '''
$MYVAR

Pergunta - por que a segunda abordagem via variável não funciona? Como fazer isso funcionar?

    
por Sergey Alaev 13.07.2014 / 15:43

2 respostas

2

Como explicou @lled, o problema é que o shell processa aspas antes de expandir as variáveis, portanto, colocar cotações em variáveis não faz nada útil. Mas há algumas alternativas, dependendo de por que você deseja armazenar o comando (em vez de apenas executá-lo diretamente).

Se você quiser apenas definir o comando uma vez, use-o repetidamente, use uma função:

myfunc() {
    start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls'
}
# ...
myfunc

Se você precisar criar / selecionar o comando em um lugar e, em seguida, usá-lo em outro local, poderá usar um array bash para armazená-lo. Armazene cada "palavra" do comando como um elemento da matriz e, se você fizer referência correta a ela, essas quebras de palavras serão preservadas:

myarray=(start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls')
# ...
"${myarray[@]}"

O que acontece aqui é que a matriz é definida como tendo os elementos "start-stop-daemon", "--start", "--exec", "/ bin / su", "-", "root "," -c "e" whoami; ls ". As aspas simples não são armazenadas como parte da matriz, mas têm o efeito de tornar "whoami; ls" um único elemento da matriz. Então, na expansão da matriz, o [@] diz ao shell para expandir cada elemento da matriz em uma palavra separada, e as aspas duplas em torno dele evitam qualquer divisão adicional de palavras nos valores resultantes.

Para mais informações (e algumas outras opções), veja BashFAQ # 50: Estou tentando colocar um comando em um variável, mas os casos complexos sempre falham!

    
por 14.07.2014 / 04:53
2

O shell Bourne (e Bourne novamente) tem regras complicadas da análise de linha à execução de comandos.

Por uma questão de simplificação, vamos reduzir sua linha de comando direta para

su -c 'whoami; ls'

e seus "incorporados" para

MYVAR="su -c 'whoami; ls'"     
$MYVAR                   

No primeiro caso, a linha de comando é dividida em 3 tokens (palavras), porque o caractere de espaço é um metacaractere que delimita as palavras, e também porque aspas escapam dos metacaracteres (portanto, o espaço é escapado entre aspas). Antes da execução do comando, aplica-se um estágio de remoção de cotação, deixando três palavras e nenhuma cotação. A primeira palavra é o nome do comando su e as outras duas são -c e whoami; ls , os parâmetros do comando. Certo.

No segundo caso, a chamada consiste em uma expansão de parâmetro único. Para algumas expansões (incluindo a expansão de parâmetros), aplica-se a divisão de palavras. Para esse objetivo, uma variável especial chamada IFS é usada para deduzir palavras do valor do parâmetro expandido. Por padrão, o IFS consiste em caracteres de espaço e tabulação e em nova linha. Isso significa que cada um desses caracteres é usado para dividir um grupo de caracteres para formar palavras. Aqui, o valor expandido é:

su -c 'whoami; ls'

e acabamos com 4 palavras, a saber: su , -c , 'whoami; e ls' . Além disso, o estágio de remoção de cotação não ocorre para valores expandidos de parâmetro. O comando é emitido (com o nome do comando su e seus 3 argumentos) e você obtém uma mensagem de erro estranha.

A complexidade dessa situação é que temos que manter uma palavra contendo um espaço como um argumento para su , o próprio espaço sendo exposto ao estágio de divisão de palavras, e aspas são inúteis na expansão de parâmetros.

O que você pode fazer para lidar com isso é brincar com a variável IFS. Uma solução seria:

IFS=: MYVAR="su:-c:whoami; ls"
$MYVAR

O IFS é aqui substituído para excluir o caractere de espaço da divisão de palavras, sendo a tarefa assumida pelo recém-introduzido caractere de cólon.

    
por 13.07.2014 / 20:47