ssh
셸을 사용하지 않고 클라이언트에서 서버로 명령을 전달하는 기본 방법이 구현에 있다고 생각하지 않습니다 .
이제 원격 쉘에게 특정 인터프리터 (예 sh
: 예상 구문을 알고 있음) 만 실행하고 다른 방법으로 실행할 코드를 제공하도록 지시 하면 상황이 더 쉬워 질 수 있습니다 .
다른 평균은 예를 들어 표준 입력 또는 환경 변수 일 수 있습니다.
둘 다 사용할 수 없으면 아래에서 해키 해킹 솔루션을 제안합니다.
stdin 사용
원격 명령에 데이터를 공급할 필요가없는 경우 가장 쉬운 솔루션입니다.
원격 호스트 xargs
에 -0
옵션 을 지원 하는 명령이 있고 명령이 너무 크지 않은 경우 다음을 수행 할 수 있습니다.
printf '%s\0' "${cmd[@]}" | ssh user@host 'xargs -0 env --'
해당 xargs -0 env --
명령 행은 모든 해당 쉘 제품군과 동일하게 해석됩니다. xargs
stdin에서 널로 구분 된 인수 목록을 읽고 인수로 인수로 전달합니다 env
. 첫 번째 인수 (명령 이름)에 =
문자 가 포함되어 있지 않다고 가정합니다 .
또는 인용 구문을 sh
사용하여 각 요소를 sh
인용 한 후 원격 호스트에서 사용할 수 있습니다 .
shquote() {
LC_ALL=C awk -v q=\' '
BEGIN{
for (i=1; i<ARGC; i++) {
gsub(q, q "\\" q q, ARGV[i])
printf "%s ", q ARGV[i] q
}
print ""
}' "$@"
}
shquote "${cmd[@]}" | ssh user@host sh
환경 변수 사용
이제 클라이언트에서 원격 명령의 stdin으로 일부 데이터를 공급해야하는 경우 위의 솔루션이 작동하지 않습니다.
ssh
그러나 일부 서버 배포에서는 클라이언트에서 서버로 임의의 환경 변수를 전달할 수 있습니다. 예를 들어, 데비안 기반 시스템의 많은 openssh 배포에서는 이름이로 시작하는 변수를 전달할 수 LC_
있습니다.
이 경우 위와 같이 따옴표 붙은 코드를 LC_CODE
포함 하는 변수를 가질 수 있고 클라이언트에게 해당 변수를 전달하도록 지시 한 후 원격 호스트에서 실행할 수 있습니다 (다시 말해 모든 쉘에서 동일하게 해석되는 명령 줄입니다). sh
sh -c 'eval "$LC_CODE"'
LC_CODE=$(shquote "${cmd[@]}") ssh -o SendEnv=LC_CODE user@host '
sh -c '\''eval "$LC_CODE"'\'
모든 셸 패밀리와 호환되는 명령 줄 작성
stdin이 필요하고 sshd가 변수를 허용하지 않거나 일반 솔루션이 필요하기 때문에 위의 옵션 중 어느 것도 허용되지 않으면 모든 호스트와 호환되는 원격 호스트에 대한 명령 행을 준비해야합니다. 지원되는 쉘.
모든 껍질 (Bourne, csh, rc, es, fish)은 자체적으로 다른 구문을 가지고 있으며 특히 인용 메커니즘이 다르며 일부는 해결하기 어려운 한계가 있기 때문에 특히 까다 롭습니다.
여기에 내가 생각해 낸 해결책이 있습니다.
#! /usr/bin/perl
my $arg, @ssh, $preamble =
q{printf '%.0s' "'\";set x=\! b=\\\\;setenv n "\
";set q=\';printf %.0s "\""'"';q='''';n=``()echo;x=!;b='\'
printf '%.0s' '\'';set b \\\\;set x !;set -x n \n;set q \'
printf '%.0s' '\'' #'"\"'";export n;x=!;b=\\\\;IFS=.;set `echo;echo \.`;n=$1 IFS= q=\'
};
@ssh = ('ssh');
while ($arg = shift @ARGV and $arg ne '--') {
push @ssh, $arg;
}
if (@ARGV) {
for (@ARGV) {
s/'/'\$q\$b\$q\$q'/g;
s/\n/'\$q'\$n'\$q'/g;
s/!/'\$x'/g;
s/\\/'\$b'/g;
$_ = "\$q'$_'\$q";
}
push @ssh, "${preamble}exec sh -c 'IFS=;exec '" . join "' '", @ARGV;
}
exec @ssh;
즉 A의 perl
래퍼 스크립트 주위 ssh
. 나는 그것을 부른다 sexec
. 당신은 그것을 다음과 같이 부릅니다.
sexec [ssh-options] user@host -- cmd and its args
그래서 당신의 예에서 :
sexec user@host -- "${cmd[@]}"
그리고 랩퍼는 cmd and its args
명령 행 으로 바뀌어 모든 쉘 cmd
은 인수 를 호출하는 것으로 해석합니다 (내용에 무관심).
한계 :
- 프리앰블과 명령이 인용되는 방식은 원격 명령 행이 크게 커져 명령 행의 최대 크기에 대한 한계에 빨리 도달한다는 의미입니다.
- Bourne shell (가보 툴 체스트에서), 대시, bash, zsh, mksh, lksh, yash, ksh93, rc, es, akanga, csh, tcsh, 최근 데비안 시스템에서 발견 된 물고기 및 / Solaris 10의 경우 bin / sh, / usr / bin / ksh, / bin / csh 및 / usr / xpg4 / bin / sh
yash
원격 로그인 쉘인 경우 인수에 유효하지 않은 문자가 포함 된 명령을 전달할 yash
수 없지만 어쨌든 해결할 수 없다는 한계가 있습니다.
- csh 또는 bash와 같은 일부 쉘은 ssh를 통해 호출 될 때 일부 시작 파일을 읽습니다. 우리는 그것들이 프리앰블이 여전히 작동하도록 행동을 극적으로 변화시키지 않는다고 가정합니다.
- 옆에
sh
, 그것은 또한 원격 시스템이 가지고 가정 printf
명령을 사용합니다.
작동 방식을 이해하려면 다른 쉘에서 인용이 작동하는 방식을 알아야합니다.
- Bourne : 특별한 특성
'...'
이 없는 강력한 인용문입니다 . 백 슬래시로 이스케이프 할 수있는 "..."
약한 따옴표 "
입니다.
csh
. "
내부에서 벗어날 수 없다는 점을 제외하고는 Bourne과 동일합니다 "..."
. 또한 줄 바꿈 문자 앞에 줄 바꿈 문자를 입력해야합니다. 그리고 !
심지어는 작은 따옴표 안에 원인 문제.
rc
. 따옴표 만 '...'
(강함)입니다. 작은 따옴표 안의 작은 따옴표는 ''
(와 같은 '...''...'
) 로 입력됩니다 . 큰 따옴표 나 역 슬래시는 특별하지 않습니다.
es
. 따옴표와 달리 백 슬래시는 작은 따옴표를 이스케이프 할 수 있다는 점을 제외하고는 rc와 같습니다.
fish
: 백 슬래시가 '
내부에서 이스케이프된다는 점을 제외하고는 Bourne과 동일합니다 '...'
.
이러한 모든 제약 조건이 있으면 모든 쉘에서 작동하도록 명령 줄 인수를 안정적으로 인용 할 수 없다는 것을 쉽게 알 수 있습니다.
다음과 같이 작은 따옴표 사용 :
'foo' 'bar'
다음을 제외하고 모두 작동합니다.
'echo' 'It'\''s'
에서 작동하지 않습니다 rc
.
'echo' 'foo
bar'
에서 작동하지 않습니다 csh
.
'echo' 'foo\'
에서 작동하지 않습니다 fish
.
우리가 백 슬래시와 같은 변수에 그 문제가 문자를 저장하기 위해 관리하는 경우 그러나 우리는 주위에 대부분의 이러한 문제의 작업을 할 수 있어야한다 $b
, 단일 인용 $q
에, 줄 바꿈 $n
(과 !
에서 $x
쉘 독립적 인 방식으로 csh의 히스토리 확장).
'echo' 'It'$q's'
'echo' 'foo'$b
모든 껍질에서 작동합니다. csh
그래도 줄 바꿈에는 효과가 없습니다 . $n
에 개행 문자가 포함되어 있으면 개행 문자 로 확장 csh
할 수 있도록 작성해야하며 $n:q
다른 쉘에서는 작동하지 않습니다. 그래서, 우리가 호출하는 대신에 여기에서하고 - 결국 무엇을 sh
하고있는 sh
사람들을 확장 $n
. 이는 또한 원격 로그인 쉘과에 대한 두 가지 인용 단계를 수행해야 함을 의미합니다 sh
.
$preamble
그 코드는 까다로운 부분입니다. 그것은 단지 사람들을 정의하는 각각의 (그것이 다른 사람을 위해 설명으로 동안) 껍질의 한 해석 코드의 일부 섹션이 모든 조개의 다양한 다른 인용 규칙을 사용한다 $b
, $q
, $n
, $x
각각의 쉘 변수.
다음은 원격 사용자의 로그인 쉘에서 해석 할 수있는 쉘 코드입니다 host
.
printf '%.0s' "'\";set x=\! b=\\;setenv n "\
";set q=\';printf %.0s "\""'"';q='''';n=``()echo;x=!;b='\'
printf '%.0s' '\'';set b \\;set x !;set -x n \n;set q \'
printf '%.0s' '\'' #'"\"'";export n;x=!;b=\\;IFS=.;set `echo;echo \.`;n=$1 IFS= q=\'
exec sh -c 'IFS=;exec '$q'printf'$q' '$q'<%s>'$b'n'$q' '$q'arg with $and spaces'$q' '$q''$q' '$q'even'$q'$n'$q'* * *'$q'$n'$q'newlines'$q' '$q'and '$q$b$q$q'single quotes'$q$b$q$q''$q' '$q''$x''$x''$q
이 코드는 지원되는 쉘에서 해석 될 때 동일한 명령을 실행하게됩니다.
cmd
에/bin/sh -c
우리가 모든 경우의 99 %에서 posix shell로 끝났다면, 그렇지 않습니까? 물론 특수 문자를 이스케이프 처리하는 것은 조금 더 고통 스럽지만 초기 문제를 해결할 것입니까?