我正在尝试在远程计算机上运行下面的脚本,我在这里面临两个问题
- 此处文档中定义的 while 循环正在无限运行
- 读取回车键在远程主机上不起作用
如果没有 sshpass 命令脚本可以在本地主机上正常工作。
有人可以调查一下并提供帮助吗?
1 #!/bin/bash
2 #read -p "Enter the user name to connect to server: " user
3 #read -s -p "Enter the password : " passd
4 #sshpass -p $passd ssh -qt -o StrictHostKeyChecking=no -o ConnectTimeout=4 $user@$1 << 'MYSCR'
5 ##############################################################################################################################################
6 while :
7 do
8 clear
9 echo "Server Name - $(hostname)"
10 echo "1. Check Server Information"
11 echo "2. Check Network Information"
12 echo "q. to Exit"
13 read -p "Enter your choice [ 1 - 3 ]" choice
14 case $choice in
15 1)
16 echo "Server Date : `date`"
17 echo "Server Uptime :`uptime`"
18 read -p "Press [Enter] key to continue..."
19 readEnterKey
20 ;;
21 2)
22 echo "File system information"
23 df -hP | column -t
24 read -p "Press [Enter] key to continue..."
25 readEnterKey
26 ;;
27 q)
28 echo "Bye!"
29 exit 0
30 ;;
31 *)
32 echo "Error: Invalid option..."
33 read -p "Press [Enter] key to continue..."
34 readEnterKey
35 ;;
36 esac
37 done
38 #MYSCR
答案1
几个问题:
读取一行,它是
IFS= read -r line
.read -s -p "Enter the password : " passd
特别是对于密码包含反斜杠或以 字符开头或结尾的用户,您将无法正常工作$IFS
。所以应该是IFS= read -rsp 'Enter the password: ' passd || exit
在 bash 中,列表上下文中的参数扩展必须加引号。
"$user@$1"
,不是$user@$1
举例。听起来
-o StrictHostKeyChecking=no
有点像请破解我。除非网络可以完全信任,否则向客户端验证服务器几乎与向服务器验证客户端一样重要。该指令就像是说“相信用户就是他们所说的任何人,而不要求他们提供身份证明”如果它是在服务器端完成的。密码和其他秘密信息绝不应该在命令参数中传递,因为它们是系统内的公共信息。手册
sshpass
页对此有一个很大的警告。使用SSHPASS="$passd" sshpass -e ssh...
,或者最好仍然使用比密码身份验证更好的身份验证形式。在让变量存储秘密数据之前,最好先对其进行处理,
unset
以确保它们不会导出到环境中(并且泄漏到命令,这些命令本身可能会将它们泄漏到更远的地方;这里可能不是这种情况)。在 bash 中,那就是unset -v passd
.使用
sshpass ... ssh ... << 'HEREDOC'
,此处文档成为标准sshpass
输入,该标准输入将ssh
其传递给远程命令。在这里,您没有指定要运行哪个命令,因此它将是远程用户的登录 shell,无论它是什么。由于
ssh
的 stdin 不是终端设备,因此它会请求sshd
不要在远程端创建伪终端,即使-t
这样做也一样,因此 shell 将以非交互方式启动,这也一样好否则将开始回显提示并执行交互式 shell 在您想要执行脚本时执行的所有操作。所以在这里,该 shell 将从其标准输入读取要执行的代码。
但该代码也恰好从其标准输入中读取。那
read -p "Enter your choice [ 1 - 3 ]" choice
,将从该定理中读取。值得庆幸的是,在 shell 读取read
完整循环的代码之前while
,它不会运行,因此它只会读取此处文档中之后的内容,或者如果之后没有任何内容,它只会读取任何内容并返回eof 时失败。
bash
在这里,您需要在远程主机上运行shell(因为您使用的是 bash 特定的语法),而不是用户的登录 shell(谁会使用 bash 作为登录 shell?:-b),以非交互方式运行并传递它除了通过 stdin 之外,还有其他方式的代码。如果 ssh 服务器AcceptEnv LC_*
的配置中有一些常见的,您可以这样做:
code=$(
cat <<'EOF'
the code there
EOF
)
进而:
SSHPASS="$passd" LC_CODE="$code" sshpass -e ssh -o SendEnv=LC_CODE -qt "$user@$1" '
exec bash -c -- "$LC_CODE"'
这里假设远程主机上的登录 shell$user
理解该语法,即$var
用于引用变量的位置,并且可以使用双引号进行引用(这对于类似 Bourne 的 shell 来说是必要的,以避免 split+glob)。如果他们的登录 shell 是rc
或 衍生的 或zsh
或fish
,exec bash -c -- $LC_CODE
就足够了(尽管引号仅对类似 shell 有害rc
);如果是csh
或tcsh
:exec bash -c -- $LC_CODE:q
。
写它:
SSHPASS="$passd" LC_CODE="$code" sshpass -e ssh -o SendEnv=LC_CODE -qt "$user@$1" '
exec bash -c '\''eval -- "$LC_CODE"'\'
如果用户的登录 shell 是上述任何一个,则可以工作,因为bash -c 'eval -- "$LC_CODE"'
至少所有类 Bourne、类 rc、类 csh 和 Fish shell 都可以理解相同的代码。还有一个优点是不会在ps -Awww
远程主机的输出中公开该代码。
您可以在以下位置了解更多信息:如何在不知道远程用户的登录 shell 的情况下通过 ssh 执行任意简单命令?
SSHPASS="$passd" sshpass -e ssh -qt "$user@$1" " $code"
正如 @aviro 的答案(在 之前添加额外的空格,以or开头的$code
情况所需)仅当用户的登录 shell 恰好是 时才有效。另请注意,它在本地和远程系统的输出中公开了代码。$code
-
+
bash
ps -Awww
¹ 在这种情况下,它确实sshd
如此bash -c thatcode
,并且至少在当前版本的 openssh 中,它不这样做bash -c -- thatcode
(可能是因为并非所有 shell 都支持或需要它,--
或者更可能是因为该问题(在实践中不太可能发生)已被忽视;C 语言也有同样的问题system()
几十年来一直被忽视)
答案2
当您使用此处文档时,文本将写入标准输入(标准输入)的过程,在这种情况下 - 到标准输入你的ssh
流程。由于您将此处文档重定向到标准输入您的ssh
,它无法读取您的键盘。
而不是在标准输入ssh 的,将其分配给一个变量,然后使用该变量作为ssh
在远程主机上运行的命令。
# Read the here-document into the $COMMAND variable
read -r -d '' COMMAND << 'MYSCR'
while :
do
clear
...
MYSCR
# Run ssh with $COMMAND
sshpass -p $passd ssh -qt -o StrictHostKeyChecking=no -o ConnectTimeout=4 $user@$1 "$COMMAND"