Executando o script 'sh -c' através do SSH (passando argumentos com segurança e sanidade)

3

De repente, percebi que não sei como executar as tarefas no SSH.

Eu tentei fazer

$ ssh user@server sh -c 'echo "hello"'

mas não produz nada, ou melhor, gera uma linha vazia. Se o comando dado a ssh é executado através de $SHELL -c no host remoto, então eu posso ver porque isso é (ou eu posso tentar racionalizá-lo para mim mesmo assim mesmo).

Ok, segunda tentativa:

$ ssh user@server 'echo "hello"'
hello

Tudo bem e bom.

Agora, o que eu realmente esperava funcionaria:

$ ssh user@server 'echo "hello $1"' sh "world"
hello  sh world

Hmm ... de onde vem o sh ? Isso está indicando que $1 está vazio e que o que realmente é executado no outro lado é algo como

$SHELL -c 'echo "hello  sh world"'

e não o que eu esperava,

$SHELL -c 'echo "hello $1"' sh "world"

Existe uma maneira de transmitir argumentos com segurança ao script executado via ssh , de uma maneira sensata e sensata, que é análoga à execução

sh -c 'script text' sh "my arg1" "my arg2" "my arg3" ...

mas no host remoto?

Meu shell de login, local e remotamente, é /bin/sh .

Com segurança = Preservando espaço em branco, etc. nos argumentos.

Sanely = Nenhum escape louco de citações.

    
por Kusalananda 15.06.2018 / 17:09

1 resposta

3

A primeira coisa a entender neste processo é como o ssh manipula seus argumentos. Eu não quero dizer os argumentos para a coisa que você está tentando executar, mas argumentos de ssh. Quando você invoca ssh , os argumentos após a especificação do host remoto ( user@server ) são concatenados juntos e passados pelo shell no final remoto. Isto é importante notar, porque apenas porque seus argumentos estão divididos apropriadamente no lado local, não significa que eles serão divididos apropriadamente no lado remoto.

Para usar seu exemplo:

ssh user@server 'echo "hello $1"' sh "world"

Esses argumentos são concatenados como o comando:

echo "hello $1" sh world

É por isso que você recebe

hello  sh world

O espaço duplo entre hello e sh é porque é para lá que $1 deveria ir, mas não há $1 .

Como outro exemplo, sem o $1 , é:

ssh user@server echo "foo      bar" baz

O que resulta na saída:

foo bar baz

Isso ocorre porque os argumentos estão sendo concatenados juntos, então você acaba com o comando:

echo foo      bar baz

Como não há como contornar o comando que está sendo passado através de um shell, você precisa garantir que o que passou pode sobreviver à avaliação do shell. A maneira que eu geralmente faço isso é com printf "%q "

Por exemplo:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

O que resulta na saída:

foo      bar baz

Embora seja mais limpo e mais fácil de entender, com cmd sendo um var separado, não é necessário. O seguinte funciona da mesma forma:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

Isso também funciona bem com o exemplo do argumento do shell:

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

O que resulta na saída:

1=<my arg1> 2=<my arg2> 3=<my arg3>

Como uma solução alternativa, você pode passar seu comando como um script de shell completo. Por exemplo:

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF

Existem desvantagens para essa solução, embora seja mais difícil fazer programaticamente (gerando o documento para passar STDIN). Também porque você está usando STDIN, se você quiser que o script no lado remoto leia STDIN, você não pode (pelo menos não sem alguns truques).

    
por 15.06.2018 / 17:55

Tags