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).