Bash에서 여러 명령을 ssh하고 실행하는 가장 깨끗한 방법은 무엇입니까?


349

이미 ssh 에이전트가 설정되어 있으며 Bash 스크립트의 외부 서버에서 다음과 같은 작업을 수행하여 명령을 실행할 수 있습니다.

ssh blah_server "ls; pwd;"

이제 실제로하고 싶은 것은 외부 서버에서 많은 긴 명령을 실행하는 것입니다. 인용 부호 사이에 이러한 모든 것을 포함시키는 것은 매우 추한 일이며, 이것을 피하기 위해 실제로 여러 번 ssh'ing하는 것을 피하고 싶습니다.

괄호 또는 무언가로 묶어 한 번 에이 작업을 수행 할 수있는 방법이 있습니까? 나는 다음 라인을 따라 무언가를 찾고있다.

ssh blah_server (
   ls some_folder;
   ./someaction.sh;
   pwd;
)

기본적으로 깨끗하다면 어떤 솔루션이라도 만족할 것입니다.

편집하다

명확히하기 위해, 나는 이것이 더 큰 bash 스크립트의 일부에 대해 이야기하고 있습니다. 다른 사람들은 스크립트를 처리해야 할 수도 있으므로 깨끗하게 유지하고 싶습니다. 다음과 같은 한 줄의 bash 스크립트를 원하지 않습니다.

ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"

매우보기 흉하고 읽기가 어렵 기 때문입니다.


2
흠, 모든 것을 서버의 스크립트에 넣고 한 번의 ssh호출 로 스크립트를 호출하는 것은 어떻습니까?
Nikolai Fetissov

명령이 클라이언트 측에 의존하는 경우 @Nikolai는, 그들은 쉘 스크립트, 다음에 기록 될 수 있습니다 scp, ssh및 실행됩니다. 이것이 가장 깨끗한 방법이라고 생각합니다.
khachik

6
이것은 더 큰 bash 스크립트의 일부이므로 개인 컴퓨터에 절반을, 서버에 나머지 절반을 ssh를 통해 분할하지는 않습니다. 가능하다면 개인용 컴퓨터에서 하나의 스크립트로 실행되도록하고 싶습니다. ssh에 많은 명령을 넣을 수있는 확실한 방법이 있습니까?
Eli

가장 좋은 방법은 bash를 사용하는 것이 아니라 Perl, Python, Ruby 등을 사용하는 것입니다.
Salva

1
원격 명령을 따옴표로 묶지 않으려는 이유무엇 입니까? 따옴표 안에 원하는만큼 줄 바꿈을 사용할 수 있습니다. 표준 입력 대신 문자열을 사용하는 것은 원격 스크립트에 대한 입력을 읽는 데 표준 입력을 사용할 수 있음을 의미합니다. (유닉스에서는 문자열의 일부를 평가하기 위해 로컬 쉘을 특별히 요구하지 않는 한 작은 따옴표가 일반적으로 큰 따옴표보다 선호됩니다.)
tripleee

답변:


456

어떻게 배쉬에 대한 다음 문서 :

ssh otherhost << EOF
  ls some_folder; 
  ./someaction.sh 'some params'
  pwd
  ./some_other_action 'other params'
EOF

의견에서 @Globalz가 언급 한 문제를 피하기 위해 (원격 사이트에서 수행중인 작업에 따라) 첫 번째 줄을

ssh otherhost /bin/bash << EOF

Here 문서에서 변수 대체를 수행 할 수 있지만 인용 문제를 처리해야 할 수도 있습니다. 당신이 "제한 문자열"을 인용하는 경우 예를 들어, (예. EOF위의), 당신은 변수를 대체 할 수 없습니다. 그러나 제한 문자열을 인용하지 않으면 변수가 대체됩니다. 예를 들어, $NAME쉘 스크립트에서 위에서 정의한 경우 다음을 수행 할 수 있습니다.

ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF

대상 otherhost에 지정한 이름으로 파일을 만듭니다 $NAME. 쉘 스크립트 인용에 관한 다른 규칙도 적용되지만 너무 복잡해서 여기에 들어갈 수 없습니다.


6
이것은 내가 원하는 것과 정확히 같습니다! 어떻게 작동합니까? 설명하는 페이지로 연결되는 링크가 있습니까?
Eli

9
+1 방금 저 자신을 생각하고있었습니다. 여기에 읽을 곳이 하나 있습니다 : tldp.org/LDP/abs/html/here-docs.html
bosmacs

14
또한이 출력을 내 로컬로 가져옵니다 .stdin이 터미널이 아니기 때문에 의사 터미널이 할당되지 않습니다.
Globalz

14
명령 행이 확장되지 않도록 단어 (즉, 첫 번째 'EOF') 를 인용하는 것이 중요 할 수 있습니다 . 참조 : 남자 배쉬 | 적은 + / '여기에 문서'
ASSEM

6
Paul, 나는이 질문에 거의 200k 뷰가 있기 때문에 ssh로 스크립팅 할 때 많은 사람들이 여기에 오는 것처럼 보이기 때문에 제안합니다. 스크립팅 할 때 값을 주입해야하는 것이 일반적입니다. 다른 질문과 의견에 소음이 없다면 나는 하나만 만들어 내 길을 가고 있지만,이 단계에서는 보이지 않을 것입니다. 한 줄의 각주가 사람들에게 큰 두통을 덜어 줄 수 있습니다.
koyae

122

스크립트를 로컬에서 편집 한 다음 ssh로 파이프하십시오. 예 :

cat commands-to-execute-remotely.sh | ssh blah_server

commands-to-execute-remotely.sh위의 목록처럼 보이는 곳 :

ls some_folder
./someaction.sh
pwd;

7
이것은 인용에 문제가없는 원격 스크립트에 의해 무엇이 실행되고 있는지 정확히 알고 있다는 큰 이점이 있습니다 . 동적 명령이 필요한 경우 서브 쉘과 함께 쉘 스크립트를 사용하여 여전히 ssh로 파이프 할 수 있습니다. 즉 ( echo $mycmd $myvar ; ...) | ssh myhost, 고양이 사용법과 마찬가지로 ssh 명령 스트림으로 무엇이 들어가는 지 정확히 알 수 있습니다. 물론 스크립트의 서브 쉘은 읽기 쉽도록 여러 줄 수 있습니다 - 볼 linuxjournal.com/content/bash-sub-shells
RichVel

6
의 인수를 사용하여이 작업을 수행 할 수 있습니까 commands-to-execute-remotely.sh?
elaRosca

1
나는 이것이 paultomblin 솔루션보다 훨씬 더 유용하다고 생각합니다. 파일을 열지 않고도 스크립트를 ssh 서버로 리디렉션 할 수 있습니다. 또한 훨씬 더 읽기 쉽습니다.
Patrick Bassut

3
네,하지만 에코 또는 여기에 문서를 사용 (위 답 참조) 사용 : $localvar로컬로 정의 된 변수를 해석하기 위해 \$remotevar, 원격으로 원격으로 정의 변수를 해석하는 \$(something with optionnal args)원격 서버에서 실행 무언가의 출력을 얻을 수 있습니다. 1을 통해 ssh 할 수있는 예 (또는 여기에 표시된 것처럼 여러 ssh 명령) :echo " for remotedir in /*/${localprefix}* ; do cd \"\$remotedir\" && echo \"I am now in \$(pwd) on the remote server \$(hostname) \" ; done " | ssh user1@hop1 ssh user2@hop2 ssh user@finalserver bash
Olivier Dulac

2
흠 ... 입력 리디렉션을 사용하는 것과 어떻게 다른 ssh blah_server < commands-to-execute-remotely.sh가요?
flow2k

41

샘플 코드와 일치시키기 위해 명령을 단일 또는 이중 qoutes로 묶을 수 있습니다. 예를 들어

ssh blah_server "
  ls
  pwd
"

나는이 형식을 좋아하지만 슬프게도 std 데이터를 변수에 저장하는 데 유용하지 않습니다.
Signus

1
Signus, "표준 데이터를 변수에 저장"한다는 것은 무엇을 의미합니까?
Andrei B

1
@Signus 원격 명령 주위에 큰 따옴표 대신 작은 따옴표를 사용하거나 로컬 셸이 인터셉트하고 보간).
tripleee

큰 따옴표 코드 안에이 fileToRemove = $ (find. -type f -name 'xxx.war')와 같은 명령을 넣을 수 없습니다. fileToRemove에는 파일 이름이 있어야하지만 대신 빈 문자열이 있습니다. 탈출해야 할 것이 있습니까?
jkonst

37

두 가지 방법이 있습니다.

먼저 다음과 같이 제어 소켓을 만듭니다.

 ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>

명령을 실행

 ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>

이렇게하면 실제로 서버에 다시 연결하지 않고도 ssh 명령을 작성할 수 있습니다.

두 번째는 스크립트를 동적으로 생성하여 scp실행하고 실행하는 것입니다.


20

다음과 같이 수행 할 수도 있습니다. 명령을 스크립트에 넣고 이름을 commands-inc.sh로 지정하겠습니다.

#!/bin/bash
ls some_folder
./someaction.sh
pwd

파일을 저장하십시오

이제 원격 서버에서 실행하십시오.

ssh user@remote 'bash -s' < /path/to/commands-inc.sh

결코 실패하지 않았습니다.


내가 원래 생각했던 것과 비슷합니다! 그러나 왜 bash -s필요한가?
flow2k

또한 #!/bin/bash실제로 사용됩니까?
flow2k

2
호환성을 위해 -s가 있습니다. man bash -s에서 -s 옵션이 존재하거나 옵션 처리 후에도 인수가 남아 있지 않으면 표준 입력에서 명령을 읽습니다. 이 옵션을 사용하면 대화식 쉘을 호출 할 때 위치 매개 변수를 설정할 수 있습니다.
RJ

1
RJ, 감사합니다! 내 첫 번째 의견으로, 우리가 방금 한 ssh user@remote < /path/to/commands-inc.sh것처럼 보입니다 (나에게 도움이되는 것 같습니다). 글쎄, 귀하의 버전이 bash다른 쉘이 아닌 쉘 을 사용하도록 보장 합니다. 그것이 목적입니까?
flow2k

2
감사. 그렇습니다. 우리 중 일부는 이전 버전의 bash 및 기타 껍질에서 얻은 메모와 습관을 가지고 있습니다. :-)
RJ

10

모든 명령을 스크립트에 넣고 다음과 같이 실행할 수 있습니다.

ssh <remote-user>@<remote-host> "bash -s" <./remote-commands.sh

다소 이상하지만 잘 작동합니다. 또한 args는 스크립트로 전달 될 수 있습니다 (리디렉션 전이나 후에) 예 : 'ssh <remote-user> @ <remote-host> "bash -s"arg1 <./ remote-commands.sh arg2'예 : 'ssh <remote-user> @ <remote-host> "bash -s"- -arg1 <./ remote-commands.sh arg2 '
gaoithe

위의 다른 답변과 비슷한 질문이 있었지만 포함의 목적은 무엇 bash -s입니까?
flow2k

1
@ flow2k -s는 표준 입력에서 명령을 읽는 bash를 나타냅니다. 다음은 man페이지 의 설명입니다If the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell.
Jai Prakash

@JaiPrakash 감사합니다. 그러나 실제로 bash명령을 완전히 제거 할 수 있을지 생각하고있었습니다 ssh remote-user@remote-host <./remote-commands.sh. 나는 내 방식을 시도했지만 효과가있는 것처럼 보이지만 어떤 식 으로든 부족한지 확실하지 않습니다.
flow2k

매뉴얼 페이지에 대한 나의 이해에서 @ flow2k는 그 옵션이 없으면 어떤 결함도 없을 것입니다. 인수를 전달하려는 경우에 필요합니다. -감사합니다
Jai Prakash

9

Bash에서 SSH 및 여러 명령 실행

문자열 내에서 세미콜론으로 명령을 분리하고 에코로 전달하고 모두 ssh 명령으로 파이프합니다. 예를 들면 다음과 같습니다.

echo "df -k;uname -a" | ssh 192.168.79.134

Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda2       18274628 2546476  14799848  15% /
tmpfs             183620      72    183548   1% /dev/shm
/dev/sda1         297485   39074    243051  14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux

arnab은 앞으로 코드의 기능을 간략하게 설명하십시오. 그런 다음 작동 방식에 대해 이야기하고 코드를 입력하십시오. 그런 다음 코드를 코드 블록에 넣으면 쉽게 읽을 수 있습니다.
Eric Leschinski 1

질문에서, 당신은 OP가 피하고 싶은 것을 정확하게 제공했다 : "나는 한 줄로 bash 스크립트를 갖고 싶지 않다 ..."
jww

6

나처럼 여기에서 걸려 넘어지는 사람에게는 세미콜론과 줄 바꿈을 피하는 데 성공했습니다.

첫 번째 단계 : 세미콜론 이런 식으로 ssh 명령을 중단하지 않습니다.

ssh <host> echo test\;ls
                    ^ backslash!

원격 호스트 / home 디렉토리 (루트로 로그인)를 나열한 반면

ssh <host> echo test;ls
                    ^ NO backslash

현재 작업 디렉토리를 나열했습니다.

다음 단계 : 줄 바꿈 :

                      v another backslash!
ssh <host> echo test\;\
ls

이것은 다시 원격 작업 디렉토리를 나열했습니다-향상된 형식 :

ssh <host>\
  echo test\;\
  ls

여기보다 훨씬 좋은 점은 파선 주위에 문서 나 인용 부호를 사용하는 것입니다.

(bash, Ubuntu 14.04 LTS 사용)


1
더 좋은 점은 && 및 || 이 방법도-에코 테스트 \ & \ & ls
Miro Kropacek

@MiroKropacek 저도 좋습니다. 보다 나은? 원하는 것에 따라 다릅니다. 두 번째 명령을 조건부로 실행 한 다음 예, 무조건 실행합니다 (첫 번째 명령의 성공 또는 실패 여부와 상관없이).
Aconcagua

@MiroKropacek, 명령 주위에 큰 따옴표를 넣을 때 &&를 벗어날 필요가 없었습니다.ssh host "echo test && ls"
not2savvy

5

여러 줄 문자열과 여러 bash 스크립트를 사용하여 게시 된 답변은 저에게 효과적이지 않았습니다.

  • 긴 여러 줄 문자열은 유지 관리하기가 어렵습니다.
  • 별도의 bash 스크립트는 로컬 변수를 유지하지 않습니다.

다음은 로컬 컨텍스트를 유지하면서 여러 명령을 ssh하고 실행하는 기능적인 방법입니다.

LOCAL_VARIABLE=test

run_remote() {
    echo "$LOCAL_VARIABLE"
    ls some_folder; 
    ./someaction.sh 'some params'
    ./some_other_action 'other params'
}

ssh otherhost "$(set); run_remote"

3
누군가가 이것을 원하는 유일한 이유는 명령 줄에 앉아있는 것입니다. rundeck, jenkins 등과 같은 도구를 사용하여 분산 환경에서 명령을 실행하는 것과 같이이 질문에 대한 다른 일반적인 이유도 있습니다.
Charles Addis

이 작동하지만 원치 않는 오류 메시지가 나타납니다
Annahri

5

긴 명령이 가장 깨끗하지만 확실하게 가장 쉬운 지 확실하지 않습니다.

ssh user@host "cmd1; cmd2; cmd3"

단순성 최고!
not2savvy

4

다른 파일을 포함 할 필요가 없으므로 스크립트 작성에 효과적입니다.

#!/bin/bash
ssh <my_user>@<my_host> "bash -s" << EOF
    # here you just type all your commmands, as you can see, i.e.
    touch /tmp/test1;
    touch /tmp/test2;
    touch /tmp/test3;
EOF

"$ (which bash) -s"부분은 원격 시스템이 아닌 로컬 시스템의 bash 위치를 제공합니다. 대신 '$ (which bash) -s'를 원한다고 생각합니다 (작은 따옴표는 로컬 매개 변수 대체를 억제합니다).
jsears

1
Paul Tomblin은 6 년 전에 Bash Here Document 답변을 제공했습니다 . 이 답변은 어떤 가치를 더합니까?
jww

-s플래그, 나는 당신이 첫 번째 줄을 대체하는 것을 피할 수 있다고 그에게 인용했다 .
sjas

4

멀티플렉싱에서 기본적으로 단일 ssh 세션을 사용하도록 시스템을 구성하는 가장 쉬운 방법입니다.

소켓을위한 폴더를 생성하면됩니다 :

mkdir ~/.ssh/controlmasters

그런 다음 .ssh 구성에 다음을 추가하십시오.

Host *
    ControlMaster auto
    ControlPath ~/.ssh/controlmasters/%r@%h:%p.socket
    ControlMaster auto
    ControlPersist 10m

이제 코드를 수정할 필요가 없습니다. 이를 통해 여러 세션을 만들지 않고도 ssh 및 scp를 여러 번 호출 할 수 있으므로 로컬 컴퓨터와 원격 컴퓨터간에 더 많은 상호 작용이 필요할 때 유용합니다.

@terminus의 답변 덕분에 http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/https://en.wikibooks.org / 위키 / OpenSSH를 / 요리 책 / 멀티플렉싱 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.