SSH循环连接

Wesley13
• 阅读 649

先来看一段shell脚本:

theIp="1.1.1.1"
echo $theIp | while read ip; do
    ssh -t root@$ip
done
#
while read ip; do
    ssh -tt root@$ip
done < <(echo $theIp)
#
while true; do
    ssh root@$theIp
    exit
done

经测试发现,只有第三种写法才可以使用SSH连接,另外两种都会自动退出

  • 会报错:tcgetattr: Invalid argument,可以登入远程机器,但是无法操作,只能通过ctrl+c退出并提示:Killed by signal 2
  • 如果不加参数“-t”,则会提示:Pseudo-terminal will not be allocated because stdin is not a terminal. 然后退出登录
  • 第二种方式与第一种表现一样,但是必须加参数“-tt”,用“-t”会自动退出

综上所述,以上前两种方法完全不科学,用while true只可以登录一台,不能连续登录多台(虽然几乎没有这种需求…)

于是只好在SF上向高人提问:Why can't ssh connect to any host inside a while loop?

ssh was eating up your loop's input. Probably in this case your ssh session exits when it gets EOF from it. That's the likely reason but some input may also cause it to exit. You have to redirect its input by specifying < /dev/null or use -n

@konsolebox回答说,ssh会把while循环里的输入给吃掉!

解决方法是把输入重定向,查看manual:

-n    Redirects stdin from /dev/null (actually, prevents reading from stdin). This must be used when ssh is run in the background. A common trick is to use this to run X11 programs on a remote machine. For example, ssh -n shadows.cs.hut.fi emacs & will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically forwarded over an encrypted channel. The ssh program will be put in the background. (This does not work if ssh needs to ask for a password or passphrase; see also the -f option.)

将输入重定向到/dev/null,防止从标准输入流中读取数据。ssh在后台执行的时候必须使用该参数,亦或远程执行X11程序.

例如使用-n执行远程emacs的时候,X11连接会自动转向一个加密的通道里,并且ssh会被保持在后台。

当然,不使用-n参数,而直接将输入重定向至null也是可以的,如:ssh ip "cmd" < /dev/null

参考:shell中使用while循环ssh的注意事项

然后他还提出了一个使用文件描述符的解决方案来避免ssh读取标准输入:

while read -u 4 ip; do
    ssh root@$ip
    exit
done 4< <(echo $theIp)

BASH_BUILTINS中查到,read -u的参数意为:Read lines from file descriptor fd instead of the standard input.

上面代码中while read -u 4表示从文件描述符4读取数据到while循环的ip变量中,然后执行ssh操作,这样就避免了ssh直接读取标准输入,从而可以正常连接远程主机。

再来看看“4< <(echo ...)”的写法(注意中间有空格)。从文件描述符中读取数据有两种办法:

echo "a b" > test
#创建文件描述符3指向文件test
exec 3< test
#查看fd3的内容
cat <&3
#从fd3中读取数据到变量a和b
read -u 3 a b
echo $a,$b
#使用过程替换将标准输入转化为fd4
exec 4< <(echo "e f")
#一步到位
read -u5 a b 5< <(echo "ss zz")
#关闭fd3-5
eval "exec "{3..5}"<&-;"
  • 以上脚本的前面部分创建了一个一次性的只读文件描述符:

使用 " exec descriptor<file Name " 的模式创建只读模式的用户自定义文件描述符; 使用 "&descriptor" 的模式引用文件描述符; 只读方式的文件描述符只能读取一次,要再次读取,需要对文件描述符重新打开赋值

The process list is run with its input or output connected to a FIFO or some file in /dev/fd. The name of this file is passed as an argument to the current command as the result of the expansion. If the >(list) form is used, writing to the file will provide input for list. If the <(list) form is used, the file passed as an argument should be read to obtain the output of list. Note that no space may appear between the < or > and the left parenthesis, otherwise the construct would be interpreted as a redirection.

包括两种用法: <(list) 或 >(list)

进程列表执行时会将输入或输出连接到一个FIFO命名管道或一个在“/dev/fd”中的文件。该文件的名称作为参数传递到当前命令作为扩展的结果。

如果是“ >(list)”方式则写入该文件时将给进程列表提供输入流。若“ <(list)“方式被使用时,作为参数传递的文件应被理解为获取进程列表的输出。

注意,<、>都不应该出现空格和左括号,否则该结构将被解释为重定向。下面有个简单的用法示例:

SSH循环连接

查看下表可知“/dev/fd/63”就是63文件描述符的复制品。每次创建fd都会在这个目录下面产生一个对应的文件,被读取一次后内容就消失了…

文件

说明

 /dev/stdin

 文件描述符 0 的复制品

 /dev/stdout

 文件描述符 1 的复制品

 /dev/stderr

 文件描述符 2 的复制品

 /dev/fd/n

 文件描述符 n 的复制品

 /dev/tcp/host/port

 Bash 在 port 打开到 host 的 TCP 连接

 /dev/udp/host/port

 Bash 在 port 打开到 host 的 UDP 连接

附录:关于bash中一次性给多个变量赋值--命名管道的使用

用while read的方式可以同时赋值多个管道内的变量,但是这些变量只存在于子shell,无法改变父进程中的变量值,

因此还可以使用文件描述符的方式(原理同命名管道)给多个外部变量赋值:read -u5 a b c d 5< <(echo "a b c d")

参考:关于 bash shell 重定向的几个特殊文件Reads from the file descriptor (fd)

Linux Shell 文件描述符 及 stdin stdout stderr 重定向

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
Java执行shell脚本并返回结果两种方法的完整代码
Java执行shell脚本并返回结果两种方法的完整代码简单的是直接传入String字符串,这种不能执行echo或者需要调用其他进程的命令(比如调用postfix发送邮件命令就不起作用)执行复杂的shell建议使用String\\方式传递(对外可以封装后也传入String字符串)。/运行shell脚本
Stella981 Stella981
2年前
Linux记录
!/bin/bashmenu(){echo"欢迎使用mysql管理服务程序"echo"1.启动服务"echo"2.停止服务"echo"3.重启服务"echo"4.查看状态"echo"5.退出程序"echo
Stella981 Stella981
2年前
Shell函数返回值
Shell函数返回值shell函数返回值一般有3种方式:1.return语句shell函数的返回值可以和其他语言的返回值一样,通过return语句返回。比如:!/bin/bashfunctionmytest(){echo"mytestfunction"echo
Stella981 Stella981
2年前
PHP巧用call_user_func避免全局变量污染
我们在编写复杂的业务逻辑中,经常需要对数组做循环处理。例如:$array'crazymus','tom','jim';foreach($arrayas$key$value){echo$key,PHP_EOL;echo$value
Stella981 Stella981
2年前
Laravel 控制器的request
publicfunctionrequest1(Request$request){//取值$nameRequest::input('name');//是否有值if($requesthas('name')){echo$requestinput('name');}$res
Wesley13 Wesley13
2年前
PHP实现依赖注入
高层模块不应该依赖于底层模块,两个都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。首先,我们来看一段代码:classA{publicfunctionecho(){echo'A'.PHP_EOL;
Stella981 Stella981
2年前
PHP 隐式转换(自动装换) 和 bc 精度函数
<?phpecho1true;//true1echo'<hr/';echo1false;//false0echo'<hr/';echo1.1false;echo'<hr/';echo1.1tru
Wesley13 Wesley13
2年前
38条技巧优化PHP代码,来复习总结下吧
1、如果一个方法能被静态,那就声明它为静态的,速度可提高1/4;2、echo的效率高于print,因为echo没有返回值,print返回一个整型;3、在循环之前设置循环的最大次数,而非在在循环中;4、销毁变量去释放内存,特别是大的数组;5、避免使用像\_\_get,\_\_set,\_\_autoload等魔术方法
Stella981 Stella981
2年前
Linux系统Shell编程——脚本编写思路与过程
!(https://oscimg.oschina.net/oscnet/b5650333a00146298052e4da35a0746d.gif"兔子红箭头引导关注")Linux系统Shell编程——脚本编写思路与过程“前段时间有小伙伴问我一些问题,涉及到shell脚本的编写问题,事后,我深入思考了下,实际生产环境的确也