SSH를 통해 일부 파일을 백업하려고했지만 파일 대신 tar
'내 홈 폴더를 얻었습니다. 나는 몇 가지 추가 테스트를 수행했으며 다음과 같이 요약됩니다.
ssh root@server /bin/sh -c "cd /boot && ls -l"
내 놀랍게도 /root
아닌 파일을 나열합니다 /boot
. 그러나 /bin/sh
터미널에서 전체 명령을 실행 cd
하면 /boot
파일이 올바르게 인쇄 됩니다.
여기서 무슨 일이야?
SSH를 통해 일부 파일을 백업하려고했지만 파일 대신 tar
'내 홈 폴더를 얻었습니다. 나는 몇 가지 추가 테스트를 수행했으며 다음과 같이 요약됩니다.
ssh root@server /bin/sh -c "cd /boot && ls -l"
내 놀랍게도 /root
아닌 파일을 나열합니다 /boot
. 그러나 /bin/sh
터미널에서 전체 명령을 실행 cd
하면 /boot
파일이 올바르게 인쇄 됩니다.
여기서 무슨 일이야?
답변:
ssh를 사용하면 원격 호스트의 execvp에 전달할 일련의 인수로 명령을 정확하게 지정할 수 없습니다. 대신 모든 인수를 문자열로 연결하고 원격 쉘을 통해 실행합니다. 이것은 내 의견으로는 ssh의 주요 디자인 결함으로 두드러집니다 ... 대부분의 방식으로 잘 작동하는 유닉스 도구이지만 명령을 지정할 때 argv 대신 단일 모 놀리 식 문자열을 사용하기로 결정했습니다. 그것은 MSDOS 또는 무엇인가를 위해 설계되었습니다!
ssh는에 단일 문자열로 명령을 전달하므로 자신 만의을 sh -c
제공 할 필요는 없습니다 sh -c
. 당신이 할 때 결과는
sh -c '/bin/sh -c cd /boot && ls -l'
원래 인용문이 손실되었습니다. 따라서 명령 &&
은 다음과 같이 구분됩니다 .
`/bin/sh -c cd /boot`
`ls -l`
이들 중 첫 번째는 "cd"및 $0
= 명령 텍스트로 쉘을 실행합니다 "boot"
. "cd"명령이 성공적으로 완료 $0
되고 관련이 없으며, /bin/sh -c
성공을 나타내면 ls -l
발생합니다.
ssh
행동하는 것은 불행한 일이지만 , 그렇지 않은 경우에는 호출 sexec
하지 말아야 ssh
합니다. (그 점에서 동일하게 동작하는) 및 ( 명령 줄을 통과하지 않을 때 호출 ) ssh
의 보안 버전입니다 . 불행히도 그것이 구현되지 않았다는 것입니다. rsh
rlogin
rsh
rlogin
rexec
"`printf "%q " "$@"`"
bash printf
와 함께 쉘 이스케이프가있는 문자열을 인용하십시오.
인용 문제입니다. Ssh는 이미 쉘에 전달한 명령을 실행합니다. 여러 매개 변수를 전달하면 매개 변수가 사이에 공백으로 연결되어 문자열을 작성합니다. 따라서 원격으로 실행중인 원격 명령은 /bin/sh -c cd /boot && ls -l
(명령의 인용 부호가 로컬 쉘에 의해 해석되었으므로 인용 부호 없음)입니다.
/bin/sh -c cd /boot
실행 /bin/sh
하고 명령을 실행 지시 cd
세트도 및 $0
에 /boot
. 이 작업이 완료되면 상위 쉘 (에 의해 시작된 쉘 sshd
)이 실행됩니다 ls -l
.
귀하의 경우 sh -c
원격 쉘 ( /etc/passwd
또는 다른 암호 데이터베이스에 표시 된) 이이 명령을 이해하지 않으면 완전히 쓸모없는 것을 제거하십시오 .
ssh root@server "cd /boot && ls -l"
다른 쉘을 호출해야하는 경우에 의해 호출 된 원격 쉘에 의한 확장으로부터 보호하기 위해 원격 명령을 인용해야합니다 sshd
. 예를 들어, 로그인 쉘이 대시이고 bash 명령을 실행하려는 경우 :
ssh root@server 'bash -c "cd ~bob && ls -l"'
옵션이 쉘에 의해 구문 분석되는 방법과 더 관련이 있다고 생각합니다. 예를 들어, 다음과 같이 작동합니다.
$ ssh root@server /bin/sh -c '"cd /boot && ls -l"'
이것은 당신의 명령과 같은 문제가 있습니다 :
$ ssh root@server /bin/sh -c 'cd /boot && ls -l'
-v
스위치를 활성화하면 현재 상황 을 ssh
확인할 수 있습니다.
첫 번째 명령 :
debug1 : 명령 전송 : / bin / sh -c "cd / boot && ls -l"
두 번째 명령 :
debug1 : 명령 전송 : / bin / sh -c cd / boot && ls -l
일반적으로 명령을 통해 보낼 때 ssh
다양한 레이어가 제거하기 때문에 따옴표 안에 따옴표로 묶고 따옴표를 묶어야합니다. 또한을 보내지 않아도 /bin/sh
됩니다.
ssh
다음과 같은 인용문을 이해하면 매우 유용한 작업을 수행 할 수 있습니다 . 이것은 원격 서버에서 명령을 실행하지만 명령을 실행 한 시스템에서 로컬로 파일로 결과를 수집합니다 ssh
.
$ ssh root@server 'free -m' > /tmp/memory.status
또는 원격 서버의 디렉토리를 tar하고 로컬 시스템에서 디렉토리를 작성하는 위치 :
$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz
일반적으로 사용할 쉘을 지정할 필요가 없습니다. 이것은 작동합니다 :
ssh user@host "cd /boot && pwd"
그러나 기본 쉘에 문제가 있으면 쉘 호출을 포함하여 전체 명령 을 인용하십시오 . 다음 작품 중 하나
ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""
cd /boot && pwd
주요 패밀리 (Bourne, csh, rc)의 모든 쉘에서 작동 하지만 작동 하지 않습니다. /bin/sh -c "cd /boot && pwd"
rc
"
ssh root@server /bin/sh -c "ls -l /boot"
않습니까?