SSH를 통해 이 작업을 수행하는 방법을 전혀 몰랐다는 사실이 떠올랐습니다.
나는 ~하려고 노력한다
$ ssh user@server sh -c 'echo "hello"'
그러나 아무것도 출력하지 않습니다. 오히려 빈 줄을 출력합니다. 주어진 명령이 ssh
원격 호스트에서 실행되면 왜 이런 일이 발생하는지 이해할 수 있습니다(또는 스스로 합리화하려고 노력할 수 있습니다).$SHELL -c
좋습니다. 두 번째 시도입니다.
$ ssh user@server 'echo "hello"'
hello
모두 괜찮습니다.
이제 내가 정말로 원하는 것은:
$ ssh user@server 'echo "hello $1"' sh "world"
hello sh world
흠... 어디서 나온 거야 sh
? 이는 비어 있음을 나타내며 $1
실제로 반대편에서 실행되는 것은 다음과 같습니다.
$SHELL -c 'echo "hello sh world"'
내가 바라던 것 대신에,
$SHELL -c 'echo "hello $1"' sh "world"
ssh
다음을 통해 실행되는 스크립트에 인수를 안전하게 전달하는 방법이 있습니까?
sh -c 'script text' sh "my arg1" "my arg2" "my arg3" ...
하지만 원격 호스트에서는요?
내 로컬 및 원격 로그인 쉘은 모두 /bin/sh
.
안전 = 매개변수에 공백 등을 유지합니다.
Sanely = 따옴표를 미친 듯이 탈출하지 않습니다.
답변1
이 과정에서 가장 먼저 이해해야 할 것은 ssh가 매개변수를 처리하는 방법입니다. 실행하려는 매개변수가 아니라 ssh의 매개변수를 의미합니다. 를 호출하면 ssh
원격 호스트 사양( ) 뒤에 오는 인수가 user@server
연결되어 원격 측 셸을 통해 전달됩니다. 매개 변수가 로컬 측에서 올바르게 분할되었다고 해서 원격 측에서도 올바르게 분할된다는 의미는 아니기 때문에 주목할 가치가 있습니다.
귀하의 예를 사용하여 :
ssh user@server 'echo "hello $1"' sh "world"
이러한 매개변수는 명령으로 연결됩니다.
echo "hello $1" sh world
그게 네가 얻는 이유야
hello sh world
hello
및 사이의 이중 공백은 원래 있어야 할 곳이지만 그렇지 않기 sh
때문입니다 .$1
$1
가 없는 또 다른 예는 $1
다음과 같습니다.
ssh user@server echo "foo bar" baz
결과는 다음과 같습니다.
foo bar baz
이는 매개변수가 서로 연결되어 있으므로 다음 명령이 실행되기 때문입니다.
echo foo bar baz
셸을 통해 전달된 명령을 우회할 수 있는 방법이 없기 때문에 전달한 내용이 셸 평가에서 살아남는지 확인하기만 하면 됩니다. 내가 보통 이것을 달성하는 방법은 다음과 같습니다printf "%q "
예를 들어:
cmd=(echo "foo bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"
결과는 다음과 같습니다.
foo bar baz
별도의 변수를 사용하는 것이 더 깔끔하고 이해하기 쉽지만 cmd
필수는 아닙니다. 다음은 같은 방식으로 작동합니다.
ssh user@server "$(printf "%q " echo "foo bar" baz)"
이는 쉘 매개변수 예에도 적용됩니다.
cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"
결과는 다음과 같습니다.
1=<my arg1> 2=<my arg2> 3=<my arg3>
대체 솔루션으로 명령을 완전한 셸 스크립트로 전달할 수 있습니다. 예를 들어:
ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF
그러나 이 솔루션에는 단점도 있습니다. 프로그래밍 방식으로 수행하는 것이 더 어렵기 때문입니다(STDIN을 통과하도록 문서 생성). 또한 STDIN을 사용하고 있기 때문에 원격 측의 스크립트가 STDIN을 읽도록 하려면 (적어도 약간의 트릭을 사용하면) 그렇게 할 수 없습니다.