目录

一、SHELL

1.1 输入单个指令

1.2 输入多行指令

限制输入内容的个数

控制输入内容的可见性

二、Expect

简介

for 中嵌套 expect

在expect中使用shell的环境变量

实例

SSH登录

FTP文件同步

Linux中shell脚本如何自动输入密码


一、SHELL

1.1 输入单个指令

自动输入yes

echo "y" | yum install wget ,等同于yum -y  install wget

自动输入回车

echo -e "\n" | yum remove wget

**`echo -e` 的小知识**

若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
\a 发出警告声;
\b 删除前一个字符;
\c 最后不加上换行符号;
\f 换行但光标仍旧停留在原来的位置;
\v 与\f 相同;
\n 换行且光标移至行首;
\r 光标移至行首,但不换行;
\t 插入 tab 符号;
\\ 插入 ‘' 字符;
\nnn 插入 nnn(八进制)所代表的 ASCII 字符;

1.2 输入多行指令

输入多行指令我们需要借助输入重定向操作符 <<

以下面这个脚本为例

multi.sh

#!/bin/bashread -p "enter number:" no
read -p "enter name:" name
echo "you have entered $no, $name"

借助 << 符号进行自动化输入

#!/bin/bash
sh multi.sh << EOF
1
mutoe
EOF

但是有时候这种方法并不生效,比如 ssh-keygen 命令,那只有借助强大的 expect 命令了

read -p "you are sure you wang to xxxxxx?[y/n]" input
echo $input
if [ $input = "y" ];thenecho "ok "
fi

限制输入内容的个数

我们还可以使用 read命令的-n 选项,此选项后面需要接一个数字,可以限制输入内容的个数。

#!/bin/bash
read -p "May I ask your name: " name
echo "Hello $name"
read -n1 -p "Press any key to exit"
echo
exit 0

控制输入内容的可见性

目前,我们输入的内容都是可见的,但有些敏感的数据,如密码,信用卡号等信息,输入时并不想可见。那么可以使用read -s

这时再输入时,就有一个钥匙的标识,而且输入时不可见。

二、Expect

简介

expect是一个免费的编程工具,用来实现自动和交互式任务通信,安装 yum install -y expect

Expect的语法:

关键命令send、expect、spawn和interact:

  • send:用于向进程发送字符串,注意一定要在末尾加\r回车
  • expect:从进程接收字符串
  • spawn:启动新的进程
  • interact:允许用户继续交互

expect 有两种用法,一种是直接写 expect 解释器的脚本,和 bash 类似,以 #!/usr/bin/expect 开头

下面是一个合格的 expect 脚本示例

#!/bin/expectset IP     [lindex $argv 0] # 读取第1个参数设置为 IP 变量
set PASSWD [lindex $argv 1] # 读取第2个参数设置为 PASSWD 变量
set CMD    [lindex $argv 2] # 读取第3个参数设置为 CMD 变量spawn ssh $IP $CMD # spawn 来给命令加壳,以便于断言输出
expect { # expect 是断言命令# 如果读取到屏幕上输出 (yes/no) 信息,则输入 "yes" 并按下回车键# exp_continue 是继续等待花括号内的断言, 如果不加这一句会直接跳出 expect"(yes/no)?" { send "yes\r"; exp_continue }"password:" { send "$PASSWD\r" } # 如果读取到屏幕上输出 password 信息,则输入 PASSWD 变量中的内容"*host " { exit 1 } # 如果读取到 "No route to host" 等内容, 就以非0状态退出
}
expect eof # 等待命令执行结束

需要注意的是,在 expect 解释器内, 除了几个特定关键字的命令,其他命令都不可用,这种方式适用于执行命令较少,单次需要交互较多的自动化脚本

第二种用法是在 bash 脚本中执行 expect 配合重定向操作符, 在有大量脚本需要执行的情况下推荐使用该方式

下面是我在 certbot 命令时使用的 shell 脚本,以供参考

#!/bin/bashsudo expect << EOF
spawn certbot --nginx
expect {"Enter email address" { send "mutoe@foxmail.com\n";exp_continue}"Please read the Terms of Service" {send "A\n";exp_continue}"Would you be willing to share your email address" {send "N\n";exp_continue}"Which names would you like to activate HTTPS for" {send "\n";exp_continue}"You have an existing certificate that has exactly the same domains" {send "1\n";exp_continue}"Please choose whether or not to redirect HTTP traffic to HTTPS" {send "2\n";exp_continue}eof
}

expect是关键的部分,在英文中,expect有“期待”的意思,采用了tcl的模式-动作语法,此语法有以下几种模式:
单一分支语法:

expect "hello" {send "you said hello"}

多分支模式语法:

expect {"hello" {send "hello\r"; exp_continue}"world" {send "world\r"; exp_continue}"how are you ?" {send "Fine,thanks\r"}
}

for 中嵌套 expect

#!/bin/bash

P = "maomaochong"

for i in `seq 100 114` #因为我在自己电脑上测试所以这样写,多台可以写成`seq 100 114` 表示一个范围
do
  /usr/bin/expect <<EOF
  spawn scp - r / usr / local / zabbix - agent.tar.gz zgabe@192.168.1.$ {i}:/home/zgabe/

expect {"yes/no" { send "yes\r" } #\r : 表示确定}
  expect {"password:" { send "$P\r" }}
  expect "*$" ? #普通用户是$, root用户是#
  send "exit\r"

expect eof

EOF  #expect语句结束 注意 这个EOF一定要顶头写,前后不能有空格

done #shell的for循环结束。

https://blog.51cto.com/maomaochong/1812460

在expect中使用shell的环境变量

两种方法:

https://groups.google.com/g/comp.lang.tcl/c/qxmoNrsT0Kg
1.) 环境变量:

hostname=10.0.0.1; export hostname
expect -c '
set timeout 15

spawn telnet $env(hostname)
...
'
or:
hostname=10.0.0.1 expect -c '
... ditto with $env(hostname)
'

2.) 通过在 sh 中连接不同引用的字符串(by concatenating differently quoted strings in sh):

hostname=10.0.0.1
expect -c '
set timeout 15

spawn telnet '"$hostname"'
...
'

注意事项:

> There are two approaches:
> 1.) per environment:
> hostname=10.0.0.1; export hostname
> expect -c '
> set timeout 15
> spawn telnet $env(hostname)
> ...
> '

除非执行完expect后主动 unset hostname,否则其他程序也会读到这个变量,对于公共变量来说,你的修改会影响其他程序。

Unless you explicitly unset hostname after the call
to expect, you might pass the hostname also to other utilities
called from your script later. This is probably no problem
for the hostname, but could be for the password.

> hostname=10.0.0.1 expect -c '
> ... ditto with $env(hostname)
> '

这个例子中hostname只是当做环境变量传给expect,然后遗忘,如果你在后续sh脚本中还要用这个变量则不要用这个方法。

In this case, hostname is only passed to expect as an environment
variable, and forgotten afterwards, so if you still need the hostname
afterwards in the sh-script, don't use it this way.

hostname=10.0.0.1
hostname=$hostname expect -c ' ... '
may appear moronic, but isn't at all. It leaves hostname
as a non-exported variable, while still sending it to expect.

> 2.) by concatenating differently quoted strings in sh:
> hostname=10.0.0.1
> expect -c '
> set timeout 15
> spawn telnet '"$hostname"'
> ...
> '

这个方法是不安全的:

Actually, I'm sorry for having posted it at all, because it is maximally
unsafe. Any magic characters in the value of hostname could create havoc
inside expect, as expect sees its value as unquoted(!) part of the script
rather than as the contents of some variable or array.

For hostnames set explicitly in the shell-script this is a non-issue,
but I don't know, if this wasn't just a simplification of your problem
for posting.

方法一实例:

how to use shell variable in expect session?--https://stackoverflow.com/questions/40376587/how-to-use-shell-variable-in-expect-session

#!/bin/sh
IP_LIST=('127.0.0.x' '127.0.0.y' '127.0.0.z')
for ip_addr in "${IP_LIST[@]}"
doecho "$ip_addr"ip_addr=$ip_addr expect << 'EOS'  #这一句的ip_addr=$ip_addr不能省set timeout 10spawn scp -p /home/foo/bar/baz.txt user@$::env(ip_addr):/home/destdirexpect "*password*"send "pasword\r"expect eos; # do you mean `eof'?exit 0
EOS
done

:: 用于确保您引用的是全局环境变量。它在这里是可选的,因为它不在 proc 中。在 proc 中,如果要使用全局 var,则必须首先将其声明为全局 var 或简单地使用 $::var

"$env(MYPASSWORD)\r":

send  "$env(MYPASSWORD)\r"

MYPASSWORD 是在shell中通过export设定的环境变量

bash - 如何在expect会话中使用shell变量? - Thinbug

shell 和expect脚本中的取参数

shell 直接使用$1  $2 来取得参数

expect 使用 set name [lindex $argv   1]   set host [lindex $argv  2]

.语法的问题。由于脚本头#!只能识别一种脚本,所以怎么让shell识别expect?有一种方式是利用<<将需要的expect脚本输入到os上的expect编译器。shell脚本例子:

......shell command......

expect<<EOF

....expect command....

EOF

......shell command......

除了中间的expect command(就是我们需要的交互),其余的都是shell的相关命令语法。这里需要提醒的是,

<<EOF

内容

EOF

不要忘记这个结构。功能是将内容输出到标准输入。这里EOF可以是任何别的字符(没有测试过有没有非法字符)。

原文链接:https://blog.csdn.net/wxliu1989/article/details/21932955

实例

SSH登录

例:expect脚本ssh.exp内容:

#!/usr/bin/expect
set timeout 2
spawn ssh wan@10.229.130.107
expect {"[Pp]assword" {send "123456\r";}"[Yy]es/no" {send "yes\r";exp_continue}
}
...
...
...
send "exit\r"
exit 0#send:用于向进程发送字符串,注意一定要在末尾加\r回车
#expect:从进程接收字符串
#spawn:启动新的进程
#interact:允许用户继续交互

FTP文件同步

经常要面临在某个环境编辑代码然后需要自动同步更新到其他机器的情况。对于这种问题,我们也可以借助expect脚本来实现。

例:
配置文件为sync.cfg:

#","分隔的内容分别为需要同步的文件、目标路径和目标主机密码
/home/long/tmp/*.py,wan@10.229.130.107:/home/wan/tmp,123456

shell脚本sync_ftp.sh内容:

#!/bin/bash
#读取配置信息
for line in `cat sync.cfg`
do#删除存在的信息文件,避免数据污染if [ -f info.dat ]thenrm -rf info.datfitouch info.dat#获取路径和密码srcaddr=`echo $line | cut -d , -f 1`destaddr=`echo $line | cut -d , -f 2`passwd=`echo $line | cut -d , -f 3`#查找指定日期需要同步的文件for tmp_file in `find $srcaddr -mtime 1 -type f`doecho $tmp_file >> info.datdone#调用expect脚本进行同步expect sync_ftp.exp $destaddr $passwd#判断是否同步成功if [ $? -eq 0 ]thenrm -rf info.datecho "all success!"elseecho "sync $destaddr fail!"fi
doneexit 0

expect脚本sync_ftp.exp:

#!/usr/bin/expect
set timeout 20#读取输入参数
set remotehost [lindex $argv 0]
set remotepass [lindex $argv 1]#启动新进程,运行sftp协议
spawn sftp $remotehost
expect "[Pp]assword*"
send "$remotepass\r"#发送文件
expect "sftp>"
set content [ open info.dat ]
while  { [ gets $content local_file ] != -1 }
{send "put $local_file\r"expect "sftp>"
}
send "exit\r"
exit 0

由于文件同步是每天都需要进行的,因此可以结合定时任务crond来进一步简化工作。crond是类unix系统下用来周期执行某种任务或者事件的一个守护进程,配置信息存放在/etct/crontab文件中。crontab是系统服务crond的控制命令。关于crontab的使用,在进程章节已经进行了介绍,这里就不再赘述了。

Expect命令是很强大的,可以实现各种自动化操作。自此,你不仅可以写出各种厉害的shell脚本,还能让它们自动执行,对于日常工作的处理将会有大大的提升。

原文:自动化shell交互操作 - 知乎

注:expect命令

[set timeout 30]     
    基本上认识英文的都知道这是设置超时时间的,现在你只要记住他的计时单位是:秒   
    
[spawn ssh -l username 192.168.1.1]

spawn command命令会fork一个子进程去执行command命令,然后在此子进程中执行后面的命令;

spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。

它主要的功能是给ssh运行进程加个壳,用来传递交互指令。   
   
[expect "password:"]   
    这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒   
   
[send "ispass\r"]   
    这里就是执行交互动作,与手工输入密码的动作等效。   
    温馨提示: 命令字符串结尾别忘记加上 “\r”,如果出现异常等待的状态可以核查一下。   
   
[interact]

执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof]

expect可以让你使用“-c”选项,直接在命令行中执行它,例如:

expect -c '
        spawn git pull
        expect {
        "Usernam*" {send "code_manager@ren001.com\n"; exp_continue }
        "Passwor*" {send "$GIT_CODE_PASSWORD\n" ; exp_continue}
        }
        '
原文链接:https://blog.csdn.net/appke846/article/details/80513099

Linux中shell脚本如何自动输入密码

自动交互方法一:利用命令的自带参数,将标准输入作为手动输入的内容

(适应于带这些参数的命令)

联想到文件重定向,在shell编程中有这样一种用法(参考Linux与UNIXSHELL编程指南 chapt 5.7):"command <

重定向操作符command <

对于需求1 要求的自动登陆ftp,并作系列操作,则可以用这种方法进行自动交互。代码如下:

[yjwan@test ~]$ ftp -i -n 192.168.21.46 <

user dbftp dbftp101

ls

EOF

下面是得到的结果:

Connected to 192.168.21.46.

220 developerjail FTP server (Version 6.00LS) ready.

331 Password required for dbftp.

230 User dbftp logged in.

Remote system type is UNIX.

Using binary mode to transfer files.

229 Entering Extended Passive Mode (|||54281|)

150 Opening ASCII mode data connection for '/bin/ls'.

total 8847424

-rw-r--r-- 1 dbftp www 9055318991 Aug 28 14:28 ewiz90.sql_2010-08-29

226 Transfer complete.

221 Goodbye.

测试可以发现,如上代码使用帐号名dbftp,密码dbftp101成功登陆了ftp服务器,并进入目录,ls出当前目录的文件。

注意事项:

1 这里一定要加-i ,否则必定失败!!

这里ftp用的-I 参数 结束了交互式输入,因此可以用文本的方式自动地输入用户密码以及操作方式

如果不用-I 参数,那么必定要求你手动输入密码的!!那么就达不到自动登陆的要求了

你可以man ftp找到这个参数

-i 关闭多文件传送中的交互式提示。请参考 prompt、mget、mput 和 mdelete 子命令,以取得多文件传送中的提示的描述。

-n 防止在起始连接中的自动登录。否则, ftp 命令会搜索 $HOME/.netrc 登录项,该登录项描述了远程主机的登录和初始化过程。请参考 user 子命令。

2 举一反三:只要是要求输入密码的命令,一般都带有一个参数,允许你从标准输入,输入用户密码,以及操作。

其他常用的自动登陆命令的参数

1) Passwd命令

Linux下 Passwd有参数

--stdin

This option is used to indicate that passwd should read the new password from standard input, which can be apipe.

所以linux下自动改变用户密码的办法就是

Echo 密码 |passwd –stdin 用户名

Freebsd没有以上参数 注意

他的方法是echo passwd |pw mod user username -h 0

2)smbpasswd

-s

This option causes smbpasswd to be silent (i.e. not issue prompts)

and to read its old and new passwords from standard input, rather

than from /dev/tty (like the passwd(1) program does). This option

is to aid people writing scripts to drive smbpasswd

3)sudo

在shell脚本中需要用root用的来执行指令:

sudo 自动输入密码

echo "password" | sudo -S netstat -tlnp

-S

The -S (stdin) option causes sudo to read the password from the standard input instead of the terminal device.

3 标准输入还可以用以下两种写法:

可以将下面的内容写入到一个文本

然后ftp -I –n ip

或者echo “..” |ftp –I –n ip

都是可以的!

因为他们都满足“有一个标准的输入”这个条件

4ssh的自动登陆没有这样的自动输入密码的参数

一般都是使用公钥/私钥的方式自动登录

这种文章满街都是,网上google一下ssh自动登陆就可以找到答案。

或者用下面的办法。

自动交互方法2:利用expect脚本自动登陆

这个 expect基本属于一种编程了

基本任何需要交互式登陆的场合,他都可以解决,是相当的牛逼。

只要是第一种办法解决不了的,基本都可以用这种办法。

这里不介绍。因为另写了一遍关于expect 的。

给用户SDS_Admin做免密登录

#!/bin/bash
#把本机的公钥拷贝到/etc/ceph/ceph.conf 中 all_manage_ip 指定的所以IP的主机上line=`cat /etc/ceph/ceph.conf |grep -E 'all_manage_ip'`&&HOSTS=$(echo ${line#*=}|sed s/[[:space:]]//g)OLD_IFS="$IFS"   #备份原值
IFS=","     #设分隔符为“,”for ip_addr in ${HOSTS}
doecho "$ip_addr"ip_addr=$ip_addr expect << 'EOS'set timeout 10spawn ssh-copy-id  SDS_Admin@$::env(ip_addr)expect "*password*"send "Admin@123stor\r"expect eof; #exit 0
EOS
done
IFS="$OLD_IFS"

Shell脚本read读取从键盘输入

(摘自:Shell脚本-read命令:读取从键盘输入的数据_https://blog.csdn.net/qq_37279783/article/details/125525857)

代码1 使用 read 命令给多个变量赋值

#!/bin/bash
read -p "Enter some information > " name url age
echo "网站名字:$name"
echo "网址:$url"
echo "年龄:$age"

代码3 在指定时间内输入密码

#!/bin/bash
if
    read -t 20 -sp "Enter password in 20 seconds(once) > " pass1 && printf "\n" &&  #第一次输入密码
    read -t 20 -sp "Enter password in 20 seconds(again)> " pass2 && printf "\n" &&  #第二次输入密码
    [ $pass1 == $pass2 ]  #判断两次输入的密码是否相等
then
    echo "Valid password"
else
    echo "Invalid password"
fi

【shell】实现交互|read读取键盘输入相关推荐

  1. 编写shell脚步--读取键盘输入

    文章目录 一.read--从标准输入读取输入值 1.1.选项 1.2.使用IFS间隔输入字段 二.验证输入 三.菜单 一.read–从标准输入读取输入值 内嵌命令read的作用是读取一行标准输入.此命 ...

  2. python导入模块快捷键_Python中的模块导入和读取键盘输入的方法

    导入模块 import 语句 想使用Python源文件,只需在另一个源文件里执行import语句,语法如下: ? 当解释器遇到import语句,如果模块在当前的搜索路径就会被导入. 搜索路径是一个解释 ...

  3. python输入语句-Python中的模块导入和读取键盘输入的方法

    导入模块 import 语句 想使用Python源文件,只需在另一个源文件里执行import语句,语法如下: ? 1 import module1[, module2[,... moduleN] 当解 ...

  4. python键盘输入数组_python 二维数组切割Python读取键盘输入的2种方法

    Python提供了两个内置函数从标准输入读入一行文本,默认的标准输入是键盘.如下: 1.raw_input 2.input raw_input函数raw_input() 函数从标准输入读取一个行,并返 ...

  5. 22 Python IO、打印到屏幕、读取键盘输入、打开和关闭文件、文件定位、重命名和删除文件、Python里的目录、文件,目录相关的方法

    22Python文件I/O 22.1打印到屏幕 最简单的输出方法是用print语句,你可以给它传递零个或多个用逗号隔开的表达式.此函数把你传递的表达式转换成一个字符串表达式,并将结果写到标准输出如下: ...

  6. java—IO流——读取键盘输入的字母并转换成大写字母输出在控制台上

    读取键盘输入的字母,并转换成大写输出在控制台上 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; imp ...

  7. java 读取键盘输入

    在工作中其实很少用到java读取键盘输入的情况,但是在各种网站刷题时却经常碰到,同时,在日常写一些测试方法的时候,如果通过键盘读取输入也是十分方便的,因此简要的做一个总结,方便后续查看及使用. Sys ...

  8. python二维数组换行输出_python 二维数组切割Python读取键盘输入的2种方法

    Python提供了两个内置函数从标准输入读入一行文本,默认的标准输入是键盘.如下: 1.raw_input 2.input raw_input函数raw_input() 函数从标准输入读取一个行,并返 ...

  9. python获取键盘输入_python之判断数据异常Python读取键盘输入

    Python提供了两个内置函数从标准输入读入一行文本,默认的标准输入是键盘.如下: <python之判断数据异常Python读取键盘输入>总结了关于python网站教程,对于我们来www. ...

最新文章

  1. ASP.NET 2.0中GRIDVIEW排序
  2. 5.3 计算机网络传输层之TCP协议(tcp协议特点、tcp报文段首部格式、tcp连接---三次握手、tcp连接释放---四次握手)
  3. 构想:中文文本标注工具(内附多个开源文本标注工具)
  4. Linux中exit与_exit的区别
  5. RMI(Remote Method Invocation,远程方法调用)
  6. pe系统怎么加服务器raid驱动,在PE中添加sata-raid驱动的方法:u盘启动盘制作
  7. COM06-Can通信协议栈架构【最高峰】
  8. 【机器学习】CART决策树原理及python实现
  9. MessageQueue nativePollOnce 一个不一样的 ANR
  10. 怎么将PDF转换成jpg图片?免费方法了解一下
  11. python发送钉钉消息
  12. 三国英杰之赵云传java7723_幻世三国之赵云传奇
  13. 2018年软工第二次结对作业
  14. 现在移动端还用rem吗?nonono
  15. ffmpeg时间戳校正到相同或+1
  16. 袋鼠云研发手记 | 袋鼠云EasyManager的TypeScript重构纪要
  17. kubernetes-准入控制器-13
  18. MyBatis框架的使用及源码分析(八) MapperMethod
  19. 北京计划以环球影城为核心,开发文化旅游精品线路
  20. 苹果自带测试卡路里的软件,‎App Store 上的“h-Tracker:卡路里计算器”

热门文章

  1. 线阵相机调帧率_(转)工业相机参数之帧率相关知识详解
  2. HDOJ 2026 首字母变大写
  3. 基于分水岭算法和机载激光雷达点云三维空间分布分析的单棵树分割方法
  4. 加油吧,707!——立体几何篇
  5. Unity脚本(二)
  6. 看完后想10秒钟,你会改变自己!
  7. 该爬破解验证码,爬企信宝必须破解滑块验证
  8. AttributeError: 'module' object has no attribute 'get_frontal_face_detector'
  9. UML交流群2月14日讨论内容!
  10. xargs 如何使用?