我想使用 telnet 连接到远程主机,没有用户名/密码验证,只是
telnet remotehost
然后我需要输入一些命令进行初始化
然后我需要重复以下命令:
cmd argument
参数是从本地文件读取的,这个文件中有很多行,每一行都是一个参数,运行一个“cmd 参数”后,远程主机将输出一些结果,它可能输出一行字符串“OK”,或者输出多行,其中一行带有字符串“ERROR”,我需要根据结果做一些事情。
基本上,脚本如下:
initialization_cmd #some initial comands
while read line
do
cmd $line
#here the remote host will output results, how can I put the results into a variable?
# here I want to judge the results, like
if $results contain "OK";then
echo $line >>good_result_log
else
echo $line >> bad_result_log
fi
done < local_file
the good_result_log and bad_result_log are local files
这可能吗?谢谢!
注意:我无法控制 B,我只能在 B 上运行初始命令和 cmd $line
答案1
如果您手动启动 telnet,这实际上不是您可以做的事情 - 但是,我个人会编辑您的脚本文件并从其中启动 telnet,将来自 telnet 的输出行作为 bash 中的输入进行处理。
我实际上无法举例说明,因为我只从 Windows 脚本而不是 Bash 中完成此操作,但我知道这将轻松实现您的要求。
答案2
答案3
是的,这确实是你能我觉得是的。请参阅下面的代码示例。我确实对你描述的情况感到疑惑,但很久以前 --- 这似乎是一个经典问题。现在,因为我已经bash
好几年没碰了,我真的需要重新开始${shape}
并赚取 $$ 积分,所以我把这当作一个小练习。
您将需要两个文件,均包含在下面:(session_driver.sh
非常简化的模拟expect
)和您自己的脚本形式的控制器逻辑,controller_script.sh
以驱动您的会话。驱动程序适用于 telnet(使用 netcat 客户端)和 ssh。以下是两个连接示例,第一个是在 ssh 模式下(SSH=1)
:
NAME=root SSH=1 HOST=my-nas ./session_driver.sh controller_script.sh
my-nas
这会将您以用户身份登录到网络连接存储设备,root
以便使用 ssh 进行一些随机操作。不过,您必须手动提供密码。以下是使用默认 telnet 模式的 telnet 调用:
NAME=root PWORD='123' HOST=my-nas ./session_driver.sh controller_script.sh
在这里您指定密码“123”,PWORD
因此 telnet 会话是 100% 自动化的。
该脚本在 Linux 下测试。
会话驱动程序
/bin/bash #!/bin/bash # # BASH 远程会话驱动程序 #——用于脚本 telnet 或 rsh。 # # 参考:http://superuser.com/questions/521716/how-to-run-scripts-within-a-telnet-session # 使用情况=$(cat<<END 用法:session_driver.sh CONTROLLER_SCRIPT 其中 CONTROLLER_SCRIPT 是一个 bash 源文件,它定义了一个名为 “控制器脚本”。 环境变量必须传递值,如以下两个示例所示。 (a)通过 nc(netcat)连接到 telnet 服务器(默认): $ HOST=mybank.com NAME=me PWORD=1234 session_driver.sh milk_account.sh 密码PWORD可以省略。 (b)通过 ssh 连接到安全 shell 服务器: $ SSH=1 HOST=mybank.com NAME=me session_driver.sh milk_account.sh 在这种情况下,请不要使用 PWORD。如果需要,ssh 会提示您。 使用函数 echo_stderr、read_one_line_response 和 read_some_lines_response 在您的 CONTROLLER_SCRIPT 中。处理慢速时调整 MULTIPLE_LINES_TIMEOUT 回应。 结尾 ) script_name="SESSION_DRIVER" # 参数 如果 [[ -z "$1" ]] || (echo "$1" | grep -qE "^[-][-]?[h|H]"); 然后 回显“$USAGE” 出口 1 菲 controller_script_path=$1 # 可覆盖的参数 HOST=${HOST:?指定要连接到哪里!} NAME=${NAME:?指定用户名} PWORD=${PWORD:-} # 仅限 telnet 模式 MULTIPLE_LINES_TIMEOUT=1 # 等待每一行输出的时间 # (很快) SSH=${SSH:-0} # 默认使用 telnet # 管道 tmpdir=$(mktemp -d) # 放置管道设备的安全位置 trap 'rm -rf "$tmpdir"; echo "$script_name: all done"' EXIT # 摆脱位置 # 终止时 # 我们需要一个后门 fd,以便我们可以从服务器内部喷出输出 # 控制器到外部。 exec 3>&2 # 将 fd3 指向 fd2 的副本 (stderr) # 我们需要一个额外的管道来将服务器的输出传输回 # 服务器控制器。命名管道就可以了。这非常便于携带。 mkfifo ${tmpdir}/输出 # 设置连接命令和控制器脚本 # 确定模式。 如果 [ "$SSH" == 1 ]; 然后 cmd="ssh -t -t $HOST -l $NAME" # -t(两次)强制 tty 模拟 别的 cmd="nc -t $HOST 23" 菲 # 读取控制器脚本。 如果 [[ !-f "$controller_script_path" ]]; 然后 echo“$script_name:脚本\“$controller_script_path\”未找到 出口 1 菲 源“$controller_script_path” 如果!(声明-f controller_script> / dev / null); 然后 echo“$script_name:脚本未定义函数‘controller_script’” 出口 1 菲 # 服务器控制器辅助功能 # 通过后门管道(fd3)发送调试信息。 函数 echo_stderr(){ 回显“$1”>&3 } # 读取预期的一行输出并将其转发到标准输出。 函数 read_one_line_response () { 本地线路 # 按照 telnet 回显的方式吃掉命令行。 读取 < ${tmpdir}/output # 现在获取响应行。 读取行 < ${tmpdir}/output echo $line | tr -d '\r' # 去掉烦人的回车符 } # 读取任意数量的输出行,只要它们出现得足够多 # 关闭(在 MULTIPLE_LINES_TIMEOUT 秒内);全部通过管道传输到标准输出。 函数 read_some_lines_response () { 本地线路 读取 < ${tmpdir}/output 当读取 -t ${MULTIPLE_LINES_TIMEOUT} 行 < ${tmpdir}/output 时;执行 回显 $line | tr -d '\r' 完毕 } 函数密码交互(){ 如果 [[ -n "$NAME" && "$SSH" == 0 ]]; 然后 # 读取到 ':' 为止的字符。我们不会得到完整的一行。希望 # 我们将看到“登录”。 while true; 执行 读取-d“:”行<${tmpdir}/output; (echo $line | grep "login" > /dev/null)&&break; 完毕 回显“$NAME” 读取 < ${tmpdir}/output 回显“$PWORD” 读取 < ${tmpdir}/output 别的 read_some_lines_response > /dev/null 菲 } # 将命令推送到标准输出上的服务器。从以下位置读取服务器的输出 # 管道。并将日志信息推送到 fd3 上。 函数控制器(){ 密码交互 controller_script # 调用作为命令行参数提供的脚本 } # 焊接在一起 # 控制器的标准输出作为命令发送到服务器。 # 服务器的标准输出和错误都转到标准输出。但由于 # 'tee',控制器还能够读取服务器的标准输出和 # 通过命名管道的错误。注意由控制器生成的后门输出 # 在 fd3 上,通过上面的“exec”文件重定向转到管道线的 stderr。 控制器 | ${cmd} 2>&1 | tee ${tmpdir}/output
控制器脚本
# session_driver 的示例控制器脚本,一种执行“expect”的 bash 方式 # 用于驱动远程 telnet 或 ssh 会话。此脚本由 #session_driver.sh 脚本。 # 注意我们在源脚本中定义的内容的使用: #-echo_stderr 通过后门发送日志信息; #-read_one_line_response,阻塞函数,获取预期输出 # 线; #-read_some_lines_response,暂时阻塞函数,获取几个 # 预期输出行数;以及 #-${tmpdir} 供临时使用。 MAX_ITERATIONS=10 # 这样我们就可以限制示例 # 列出服务器的初始命令 cat > ${tmpdir}/myinitcommands <<- EOF 回声你好 不起作用 密码 末梢血 函数控制器脚本(){ 本地响应命令行 n # 播放命令列表 当读取 cmdline 时;执行 echo_stderr "CONTROLLER_SCRIPT:执行:$cmdline" 回显“$cmdline” read_some_lines_response > /dev/null 完成 < ${tmpdir}/myinitcommands echo_stderr“CONTROLLER_SCRIPT:完成初始命令” # 多次进行愚蠢的互动 # 我们在这里仔细阅读对每个发出的命令的一行响应,并 # 做出相应反应:如果挂钟的秒数是偶数,那么我们 # 要求服务器向我们提供其当前目录列表。 对于((n=0;n<$MAX_ITERATIONS;n++));执行 # 让服务器调用日期函数来获取当前的秒数 # 分钟。 echo_stderr "CONTROLLER_SCRIPT: 执行:\"date +f %S\"" # 记录命令 echo "sleep 2; date +%S" # 执行此操作! # 读取一行输出。一般来说,需要小心 # 关于读取多少输出:读取正在阻塞! 响应=$(read_one_line_response) 如果 ((${response#?} % 2 == 0)); 然后 echo_stderr "CONTROLLER_SCRIPT: even: $response seconds" # 日志事件 echo "ls /" # 在服务器上执行 read_some_lines_response > /dev/null # 这里不需要它 别的 echo_stderr "CONTROLLER_SCRIPT: odd: $response seconds" # 日志事件 睡眠 1 菲 完毕 echo exit #终止远程会话 }