`cd` 명령이 SSH를 통해 작동하지 않는 이유는 무엇입니까?


39

SSH를 통해 일부 파일을 백업하려고했지만 파일 대신 tar'내 홈 폴더를 얻었습니다. 나는 몇 가지 추가 테스트를 수행했으며 다음과 같이 요약됩니다.

ssh root@server /bin/sh -c "cd /boot && ls -l"

내 놀랍게도 /root아닌 파일을 나열합니다 /boot. 그러나 /bin/sh터미널에서 전체 명령을 실행 cd하면 /boot파일이 올바르게 인쇄 됩니다.

여기서 무슨 일이야?


1
이것이 예가 아닌 한 왜 사용하지 ssh root@server /bin/sh -c "ls -l /boot"않습니까?
몸매

당신이 물어 보니 재밌 네요. 나는 주석을 달기 전에 그것을 시도했지만 여전히 디렉토리 목록을 얻지 못했습니다.
unxnut 2016 년

2
내가 tar를 정말로 원했기 때문에 @demure 그리고 tar / boot는 내가 원하지 않는 결과에 부트 경로를 포함 할 것이다
Ambroz Bizjak

답변:


32

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발생합니다.


2
안타깝게도 그렇게 ssh행동하는 것은 불행한 일이지만 , 그렇지 않은 경우에는 호출 sexec하지 말아야 ssh합니다. (그 점에서 동일하게 동작하는) 및 ( 명령 줄을 통과하지 않을 때 호출 ) ssh의 보안 버전입니다 . 불행히도 그것이 구현되지 않았다는 것입니다. rshrloginrshrloginrexec
Stéphane Chazelas

@StephaneChazelas에 rexec 명령이 있었습니까? 내가 아는 한 C 라이브러리 함수 일뿐입니다. rsh에 대해서는 좋은 지적입니다. 모든 사람이 이미 rsh를 사용하고있는 수많은 스크립트가 있었을 때, ssh 초기에 빠른 채택의 열쇠는 rsh를 대체하는 것입니다.

나는 과거에 그것을 분명히 사용했습니다. 다른 유닉스가 많지 않기 때문에 지금은 HPUX에 있었을 것입니다. 더 널리 퍼졌다는 인상을 받았지만 인정해야합니다.
Stéphane Chazelas 2016 년

감사. 이것은 실제로 도움이되었습니다. ssh로 터널링하고 있기 때문에. 출력으로 '1 && 2'를 얻으려면 ssh -t machine1 'ssh -t machine2 "echo \"1 && 2 \ ""'를 수행해야했습니다. 그것은 몇 시간을 죽였습니다.
ghayes

"$ @"와 같은 명령을 정확하게 전달하려면 "`printf "%q " "$@"`"bash printf와 함께 쉘 이스케이프가있는 문자열을 인용하십시오.
Sam Watkins

35

인용 문제입니다. 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"'

8

옵션이 쉘에 의해 구문 분석되는 방법과 더 관련이 있다고 생각합니다. 예를 들어, 다음과 같이 작동합니다.

$ 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

참고 문헌


4

일반적으로 사용할 쉘을 지정할 필요가 없습니다. 이것은 작동합니다 :

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"
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.