使用Bash脚本自动化Telnet会话

92

我正在使用Bash脚本自动化一些与telnet相关的任务。

自动化后,用户将不需要与telnet进行交互(也就是说,脚本将完全自动化)。

脚本大致如下:

# execute some commands on the local system
# access a remote system with an IP address: 10.1.1.1 (for example)

telnet 10.1.1.1

# execute some commands on the remote system
# log all the activity (in a file) on the local system
# exit telnet
# continue with executing the rest of the script

我遇到了两个问题:

  1. 如何从脚本中在远程系统上执行命令(无需人为干预)?

    通过一些测试代码的经验,我发现当执行 telnet 10.1.1.1 命令时,telnet 进入交互会话,然后脚本中后续的代码会在本地系统上执行。那么,我该如何在远程系统上运行这些代码而不是在本地运行呢?

  2. 我无法获取本地系统中 telnet 会话的日志文件。使用的标准输出重定向操作会在远程系统上创建一个副本(我不想复制操作来将日志复制到本地系统)。我该如何实现这个功能?

13个回答

111

虽然我建议也使用expect,但对于非交互式使用,正常的shell命令可能已经足够了。telnet在stdin上接受其命令,因此您只需要通过heredoc将命令管道传输或写入其中:

telnet 10.1.1.1 <<EOF
remotecommand 1
remotecommand 2
EOF

(编辑:根据评论,远程命令需要一些时间来处理输入或早期的SIGHUP未被telnet正确处理。在这些情况下,您可以尝试对输入进行短暂的休眠:)

{ echo "remotecommand 1"; echo "remotecommand 2"; sleep 1; } | telnet 10.1.1.1

无论如何,如果要进行交互或者其他操作,请使用expect


3
“+1 sleep works like a dream ;-)” 的意思是“sleep 像做梦一样好用;-)”。句子中的表情符号表示开玩笑或者轻松愉快的情绪。句子中的“也加入了 | tee /dev/tty |,以在屏幕上显示完整的操作过程;-)”表示在命令中添加了这段代码以将会话记录在屏幕上。 - paluh
1
在我的情况下,最有帮助的答案,但我不得不添加echo -e "command\r"; - mf_
1
如何在非交互模式下向Telnet提供密码? - Geremia
{ echo "remotecommand 1"; echo "remotecommand 2"; sleep 1; } | telnet 10.1.1.1:这个例子符合我的要求。只是想知道如果telnet失败,我们如何添加错误检查并使shell脚本exit 1 - Yash
我不得不执行 (echo "command" && echo && sleep 1) | telnet ${hostname} ${port},主要是因为第二个 echo - Trevor Boyd Smith

95

写一个 expect 脚本。

这里有一个例子:

#!/usr/bin/expect

#If it all goes pear shaped the script will timeout after 20 seconds.
set timeout 20
#First argument is assigned to the variable name
set name [lindex $argv 0]
#Second argument is assigned to the variable user
set user [lindex $argv 1]
#Third argument is assigned to the variable password
set password [lindex $argv 2]
#This spawns the telnet program and connects it to the variable name
spawn telnet $name 
#The script expects login
expect "login:" 
#The script sends the user variable
send "$user "
#The script expects Password
expect "Password:"
#The script sends the password variable
send "$password "
#This hands control of the keyboard over to you (Nice expect feature!)
interact

运行:

./myscript.expect name user password

2
“expect” 主要用于交互式会话。我想要的是自动化的非交互式会话。我面临的问题是,我无法在远程系统上运行脚本(通过 telnet),因为在“_telnet 10.1.1.1_”之后的所有行都在本地机器上执行,而不是我正在访问的机器上执行。 - khan
3
正如 Thomas Telensky 所指出的,根据您的需求,ssh 最终会更容易。但是 expect 可以完全非交互式。我假设您的意思是,telnet 后面的所有行都是您想要在远程机器上执行的命令,在这种情况下,只需将它们添加到 expect 脚本中作为发送命令,并删除交互命令即可。但 ssh 更加简单和安全。 - joemooney

51

Telnet经常在学习HTTP协议时使用。我曾经将它作为我的网络爬虫的一部分使用:

echo "open www.example.com 80"
sleep 2
echo "GET /index.html HTTP/1.1"
echo "Host: www.example.com"
echo
echo
sleep 2

假设脚本的名称为get-page.sh,那么这将给你一个HTML文档:

get-page.sh | telnet

我希望这对某些人有所帮助 ;)


1
最有用的评论!这实际上让您通过将命令放入循环中来自动化发送命令。 - RomanM
另一篇帮助文章 http://blog.tonycode.com/tech-stuff/http-notes/making-http-requests-via-telnet - leomeurer
一行代码等效于:{ echo "GET / HTTP/1.1"; echo "Host: www.example.com"; echo; sleep 1; } | telnet www.example.com 80。 请注意,除非使用open命令,否则不需要在开头使用sleep,并且只需要一个空echo和一个sleep 1而不是2。 - baptx
但是如果您需要进行HTTPS请求,您应该使用netcat,也称为ncat或nc:{ echo“GET / HTTP / 1.1”; echo“Host:example.com”; echo; sleep 1; } | ncat --ssl example.com 443 - baptx
1
非常有帮助。这将在屏幕上输出结果。我是否可以等待来自Telnet的已知字符串,然后运行下一个命令,而不是使用sleep? - Mithun B
另一种情况是如果我遇到像这样的错误:telnet: Unable to connect to remote host: No route to host,那么我应该退出。我该如何处理呢?因此,在这里睡眠可能不是一个好选择。 - Mithun B

13
这对我很有效。我试图自动化多个需要用户名和密码的telnet登录。由于我正在将不同服务器的日志保存到我的计算机中,因此telnet会话需要无限期地在后台运行。
telnet.sh使用'expect'命令自动化telnet登录。更多信息可以在此处找到:http://osix.net/modules/article/?id=30
#!/usr/bin/expect
set timeout 20
set hostName [lindex $argv 0]
set userName [lindex $argv 1]
set password [lindex $argv 2]

spawn telnet $hostName

expect "User Access Verification"
expect "Username:"
send "$userName\r"
expect "Password:"
send "$password\r";
interact

sample_script.sh 用于通过运行 telnet.sh 为每个 telnet 会话创建后台进程。有关更多信息,请参阅代码的注释部分。

sample_script.sh

#!/bin/bash
#start screen in detached mode with session-name 'default_session' 
screen -dmS default_session -t screen_name 
#save the generated logs in a log file 'abc.log' 
screen -S default_session -p screen_name -X stuff "script -f /tmp/abc.log $(printf \\r)"
#start the telnet session and generate logs
screen -S default_session -p screen_name -X stuff "expect telnet.sh hostname username password $(printf \\r)"
  1. 使用命令“screen -ls”确保没有屏幕在后台运行。
  2. 阅读http://www.gnu.org/software/screen/manual/screen.html#Stuff以了解更多关于screen及其选项的信息。
  3. 在sample_script.sh中,'-p'选项预选并重新附加到特定窗口,通过“-X”选项发送命令,否则将出现“未找到屏幕会话”的错误。

3

您可以使用expect脚本代替bash。下面的示例展示了如何telnet到一个没有密码的嵌入式板子。

#!/usr/bin/expect

set ip "<ip>"

spawn "/bin/bash"
send "telnet $ip\r"
expect "'^]'."
send "\r"
expect "#"
sleep 2

send "ls\r"
expect "#"

sleep 2
send -- "^]\r"
expect "telnet>"
send  "quit\r"
expect eof

3

@thiton的答案很有帮助,但我想避免使用sleep命令。此外,telnet没有退出交互模式,因此我的脚本被卡住了。

我通过使用curl发送telnet命令(似乎等待响应),并显式告诉telnet退出来解决了这个问题:

curl telnet://10.1.1.1:23 <<EOF
remotecommand 1
remotecommand 2
quit
EOF

远程服务器是否需要 quitexit 或其他内容完全取决于另一端的情况。该命令被发送到远程服务器,而不是 telnet 客户端本身(除了作为输出到另一端的输入)。也许您可以进行编辑以澄清这一点? - tripleee
@tripleee 谢谢,这很有帮助。当我在telnet客户端尝试quit时,没有使用^]转义字符时,我实际上感到很惊讶它居然可以正常工作。稍后我会在答案中澄清这一点。您知道当使用telnet(或curl)时,在客户端脚本中是否有可能退出而不依赖于服务器的行为吗? - kapex
1
实际上,如果curl遵循传统的telnet转义机制,我会感到非常惊讶。根据简短的实验,它似乎确实没有这样做。https://stackoverflow.com/questions/42347271/terminate-curl-telnet-session-in-bashscript建议使用超时;手册没有提及任何关于带外命令的内容。 - tripleee

2
以下内容适用于我... 将您想要进行telnet的所有IP放入IP_sheet.txt中。
while true
read a
do
{
    sleep 3
    echo df -kh
    sleep 3
    echo exit
} | telnet $a
done<IP_sheet.txt

1
#!/bin/bash
ping_count="4"
avg_max_limit="1500"
router="sagemcom-fast-2804-v2"
adress="192.168.1.1"
user="admin"
pass="admin"

VAR=$(
expect -c " 
        set timeout 3
        spawn telnet "$adress"
        expect \"Login:\" 
        send \"$user\n\"
        expect \"Password:\"
        send \"$pass\n\"
        expect \"commands.\"
        send \"ping ya.ru -c $ping_count\n\"
        set timeout 9
        expect \"transmitted\"
        send \"exit\"
        ")

count_ping=$(echo "$VAR" | grep packets | cut -c 1)
avg_ms=$(echo "$VAR" | grep round-trip | cut -d '/' -f 4 | cut -d '.' -f 1)

echo "1_____ping___$count_ping|||____$avg_ms"
echo "$VAR"

0

以下是如何在Bash Shell/Expect中使用Telnet:

#!/usr/bin/expect
# just do a chmod 755 one the script
# ./YOUR_SCRIPT_NAME.sh $YOUHOST $PORT
# if you get "Escape character is '^]'" as the output it means got connected otherwise it has failed

set ip [lindex $argv 0]
set port [lindex $argv 1]

set timeout 5
spawn telnet $ip $port
expect "'^]'."

0
这里有一个解决方案,可以与扩展列表一起使用。这只需要bash - 一些上面的答案需要expect,而你可能无法指望expect已安装。
#!/bin/bash

declare -a  Extenders=("192.168.1.48" "192.168.1.50" "192.168.1.51") 
# "192.168.1.52" "192.168.1.56" "192.168.1.58" "192.168.1.59" "192.168.1.143")
sleep 5
# Iterate the string array using for loop
for val in ${Extenders[@]}; do
   { sleep 0.2; echo "root"; sleep 0.2; echo "ls"; sleep 0.2; }  | telnet $val
done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接