用于SSH的Spawn子shell并继续执行程序流程

时间:2011-03-03 19:48:03

标签: bash ssh

我正在尝试编写一个shell脚本,根据我的位置(home / campusA / campusB)自动执行某些启动任务。我去大学并在两个不同的校区(因此是校园A /校园B)上课。我的位置取决于我连接的无线网络。出于此脚本的目的,我们可以假设在调用脚本时我将连接到其中一个网络,并且我的脚本根据对iwconfig的调用知道我连接到哪个网络。

这就是我想要的:

cat file1 > file2 # always do this, regardless of where I am
if Im at home:
    start tweetdeck, thunderbird, skype

else if Im at campusA:
    activate the login script # I need to login on a webform before I get internet access. 
                              # I have written a script to automate this. 
                              # Wait for this script to finish before doing anything else
    myProg2 & # I want myProg2 running in the background until I shutdown my computer.

else if Im at campusB:
    ssh username@domain # this is the problematic line
    myProg2 & # I want myProg2 running in the background until I shutdown my computer.

start tweetdeck, thunderbird
close the terminal with the "exit" command

问题在于,campusB的无线网络位于防火墙后面,只有在我username@domain成功ssh后才能通过互联网访问。成功的ssh之后,我需要保持终端窗口处于活动状态,以便保持互联网访问。如果我关闭终端窗口,我将丢失互联网访问权限(这很糟糕)。

当我尝试执行ssh username@domain时,脚本会停止,因为我不退出ssh命令。我不能^C,这意味着脚本的其余部分永远不会被执行。如果我只是关闭终端窗口试图杀死ssh会话,我也会遇到同样的问题。

有些谷歌搜索将我带到了subshell,我使用的是错误或无法用来解决我的问题。那我该如何解决这个问题呢?我很感激任何帮助 - 我已经有一段时间了,我找不到任何有用的东西。如果它有所作为,我宁愿不在脚本中存储我的ssh密码

此外,对ssh电话(ssh username@domain &)进行&ampersanding似乎没有任何好处(有人可以解释原因吗?)

提前谢谢

修改

我必须澄清,ssh连接必须处于活动状态才能让我访问互联网。因此,当我关闭终端窗口时,我需要ssh连接仍然处于活动状态。

7 个答案:

答案 0 :(得分:7)

我有一个循环在6台服务器上的脚本,在后台通过ssh调用。在脚本的一部分中,有一个错误的供应商应用程序;应用程序没有正确“放开”连接。 (在后台使用ssh的脚本的其他部分工作正常)。

我发现使用ssh -t -t治愈了这个问题。也许这对你也有帮助。 (一个队友在网上发现了这个,我们花了很多时间,我从未回过头来阅读那篇暗示的文章。我们系统的手册页没有暗示这样的事情是可能的)

希望这有帮助。

答案 1 :(得分:2)

以下是一些可能会有所帮助的想法。

<强>子壳

子shell分叉新进程,但不将控制权返回给调用shell。如果你想分叉一个子shell为你做的工作,那么你需要在该行附加一个&

(ssh username@domain) &

但这似乎不是使用子shell的令人信服的理由。如果你有一个数字命令,你想按顺序执行,但是与调用shell并行执行,那么也许它是值得的。例如......

(dothis.sh; thenthis.sh; andthislastthingtoo.sh) &

<强>分岔

我不确定为什么&不适合你,但也可能值得研究nohup。这使命令“免疫”挂起信号。

nohup ssh username@domain(在末尾尝试使用和不使用&

<强>密码

不在脚本中存储密码对于任何ssh自动化都是必不可少的。您可以使用公钥加密来实现这一点,这是ssh的固有特性。我不会在这里详细介绍,因为在设置此项目时,所有互联网上都有number of great resources。我强烈建议进一步调查。

如果你选择这条路线,我还建议在“批处理模式”下运行ssh,这将禁用密码查询,如果5分钟后没有响应,将自动与服务器断开连接。

ssh -o 'BatchMode=yes' username@domain

<强>持久性

然后,如果你想坚持连接,在bash中运行一些愚蠢的循环! :)

ssh -o 'BatchMode=yes' username@domain "while (( 1 == 1 )); do sleep 60; done"

答案 2 :(得分:2)

您可能想尝试将背景myProg2加倍,以将其与tty:

分离
# cf. "Wizard Boot Camp, Part Six: Daemons & Subshells",
# http://www.linux-mag.com/id/5981
(myProg2 &) &

另一种选择可能是使用libslack包中的守护进程工具:

http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script/

答案 3 :(得分:2)

在背景shell上有一个带有pseudy tty的ssh

除了@ shellter的答案之外,我想要精确一些:

@shelter说:

  

我们系统的手册页没有暗示这样的事情是可能的

我的系统Debian 7 GNU/Linux上,如果我点击:

man -Pcol\ -b ssh| grep -A3 '^ *-t '

我能读到:

     -t      Force pseudo-tty allocation.  This can be used to execute arbi‐
             trary screen-based programs on a remote machine, which can be
             very useful, e.g. when implementing menu services.  Multiple -t
             options force tty allocation, even if ssh has no local tty.

是: 多个-t选项强制tty分配,即使ssh没有本地tty。

这意味着:如果您远程运行需要访问 pseudo terminal pty (例如/dev/pts/0)的工具,可以使用-t开关来运行它们。

但是只有从{em> shell控制台(也就是拥有自己的pty)运行ssh时,这才有效。如果您计划运行它们 shell会话没有控制台,例如后台脚本,您可以使用 Multiple -t ssh强制执行伪tty 分配。

一个ssh连接上的多个ssh shell

除了@tommy和@geekosaur的答案之外,我还会有一些精确度:

@tommy指向ssh非常有用的功能。不确定这与答案有很大关系,但谈到长时间连接,必须清楚地理解这个功能。

建立连接后,ssh可以(并知道如何)在这一连接中使用它们来驱动很多事情:

  • -L允许您驱动到本地计算机/网络的远程TCP连接。 (完整语法为:-L localip:localport:distip:distport )其中可以指定localip以允许来自同一本地域的其他主机访问相同的tcp绑定,并且可以通过远程任何主机进行distip网络(不仅仅是 localhost )示例:-L192.168.1.31:8443:google.com:443允许来自本地域的任何主机通过您的主机访问 google http://192.168.1.31:8443

    < / LI>
  • -R反向相同的评论!

  • -M告诉ssh为bindind下一个ssh控制台打开 本地unix套接字 。只需打开两个终端窗口。首先在两个窗口中,点击:ssh somewhere而不是点击netstat -tan | grep :22netstat -tan | grep 192.168.1.31:22(假设 192.168.1.31 是你的onw主机的ip)

    比较关闭所有ssh会话和第一个终端,点击:ssh -M somewhere,然后点击ssh somewhere。你可能会在第二个终端看到:

    $ ssh somewhere
    + ssh somewhere
    Last login: Mon Feb  3 08:58:01 2014 from elsewhere
    

    如果现在你点击netstat -tan | grep 192.168.1.31:22(在两个oppened ssh会话中的任何一个;),你必须看到只有一个 tcp连接。

    此类功能可与-L结合使用,也可能与某些sleep 86399 ...

    结合使用

    要解决关闭每个非活动TCP连接超过120秒的tcp杀手路由器,我运行:

    ssh -M somewhere 'while :;do uptime;sleep 60;done'
    

    即使我没有按键超过两分钟,这也确保连接保持不变。

答案 4 :(得分:1)

&的问题是ssh失去了对其标准输入(终端)的访问权限,所以当它去读取要发送到另一方的内容时,它会收到错误并退出,或者是{{系统使用kill来隐式挂起它。 SIGTTIN-n选项用于处理此问题:-f告诉它不要使用标准输入,-n告诉它设置任何必要的隧道等,然后关闭终端流。

所以最好的方法就是做

-f

然后在注销前手动终止ssh -L 9999:localhost:9999 -f host & # for some random unused port 。可替换地,

ssh

(重定向是为了确保ssh -L 9999:localhost:9999 -n host 'while :; do sleep 86400; done' </dev/null & 无论如何都不会发生。)

当您处于此状态时,您可能希望保存进程ID并将其从SIGTTIN / .logout关闭:

.bash_logout

ssh -L 9999:localhost:9999 -n host 'while :; do sleep 86400; done' < /dev/null & echo $! >~.ssh_pid; chmod 0600 ~/.ssh_pid

.bash_logout

那里的额外代码试图避免有人破坏你的if test -f ~/.ssh_pid; then set -- $(sed -n 's/^\([0-9][0-9]*\)$/\1/p' ~/.ssh_pid) if [ $# = 1 ]; then kill $1 >/dev/null 2>&1 fi rm ~/.ssh_pid fi ,因为我是一个专业的偏执狂。

(代码未经测试,可能有错字)

答案 5 :(得分:0)

自从我使用ssh已经有一段时间了,我现在无法测试它,但是你尝试过-f开关吗?

ssh -f username@domain

手册页说背景ssh。不确定为什么&不起作用,但我猜它是把它解释为在远程机器上运行的命令。

答案 6 :(得分:0)

也许screen + ssh也适合这个账单?

类似的东西:

screen -d -m -S sessionName cmd

screen -d -m -S sessionName cmd &

# reconnect with
screen -r sessionName