自动交互方法一
自动交互最关键的就是交互信息的自动输入,首先联想到文件重定向,在shell编程中有这样一种用法(参考Linux与UNIX SHELL编程指南 chapt 5.7):"command << delimiter 从标准输入中读入,直至遇到delimiter分界符。"
重定向操作符command << delimiter是一种非常有用的命令,shell将分界符delimiter之后直至下一个同样的分界符之前的所有内容都作为输入,遇到下一个分界符, shell就知道输入结束了。最常见的delimiter分界符是EOF,当然完全可以自定为其他字符。
对于需求1 要求的自动登陆ftp,并作系列操作,则可以用这种方法进行自动交互。代码如下:
#!/bin/bash ftp -i -n 192.168.167.187 << EOF user hzc 123456 pwd cd test pwd close bye EOF
测试可以发现,如上代码使用帐号名hzc,密码123456成功登陆了ftp服务器,并进入目录,打印出了pwd。
五、自动交互方法二
需求2中要求采用非交互的方式改变登录用户密码,尝试用方法1,无法实现。
这时候联想到交互信息的另一个自动输入方法,管道,通过echo + sleep + | 可以实现这个需求。
#!/bin/bash (echo "curpassword" sleep 1 echo "newpassword" sleep 1 echo "newpassword")|passwd
测试通过,运行这个脚本,直接把当前用户的curpassword改成newpassword。
六、自动交互方法三
需求3中要求自动登录root账号,尝试方法1和方法2,都出现错误提示standard in must be a tty。
这时候尝试寻找外部帮助,一个shell工具expect可以实现这个功能,其实expect就是一个专门用来实现自动交互功能的工具,expect的语法可以参考相关资料,代码如下:
#!/usr/bin/expect spawn su root expect "password: " send "123456\r" expect eof exit
测试通过,运行这个脚本,直接从当前用户登录到root用户。
七、方法总结
方法一(重定向)简单直观,也经常有实际应用,但是在自动交互领域功能有限。
方法二(管道)也很简单直观,有时甚至不用sleep配合就能展现强大的自动交互实力,但是在某些时候也束手无策。
方法三(expect)在功能上是最为强大的,expect本来就是为实现自动交互功能而生,但是缺点是需要安装expect包,在嵌入式等环境下难以安装。
三个方法各有优劣,应用的好,都可以完成Linux Shell自动交互。
一、重定向
1. command << delimeter (Here Document)
2. 实例:
进入GDB,打印帮助信息并退出!
#!/bin/bash
gdb << EOF
help
quit
EOF
3. 原理
当Shell看到<<的时候,它就会知道下一个词是一个分界符。在该分界符以后的内容都被当作输入,直到shell又看到该分界符(位于单独的一行)。
二、管道
1. echo + sleep + |
2. 实例:
修改用户密码!
#!/bin/bash
(echo "root"
sleep 1
echo "root") | passwd
3. 原理
将"root"作为新密码通过管道"|"传递给passwd命令
三、命令
1. expect(系统中不存在时需要安装)
2. 实例:
ssh通过密码登录远程服务器!
#!/usr/bin/expect
spawn ssh root@192.168.10.110
expect "password:"
send "root\n"
expect eof
send "ls\n"
expect eof
send "exit\n"
expect eof
exit
3. 原理
expect为专门用于交互的命令,功能强大,但必须要先安装工具包
(1)
#!/bin/bash
set timeout 30
for i in `cat ./list.ini |grep "="|awk -F= '{print $2}'`
do
echo $i
expect<<-END
spawn ssh root@$i ""
expect "Are you sure you want to continue connecting (yes/no)? "
send "yes\n"
expect eof
exit
END
done
(2)
#!/bin/bash
set timeout 30
expect<<-END
spawn ssh-keygen -t rsa
expect "Enter file in which to save the key (/root/.ssh/id_rsa): "
send "\n"
expect "Overwrite (y/n)? "
send "\n"
expect eof
exit
END
for i in `cat ./list.ini |grep "="|awk -F= '{print $2}'`
do
spawn ssh root@$i "mkdir /root/.ssh/"
expect "password: "
send "w0iHdu77lE40FszB+QX\n"
expect eof
exit
END
expect<<-END
spawn scp /root/.ssh/id_rsa.pub root@$i:/root/.ssh/id_rsa.pub
expect "password: "
send "123\n"
expect eof
exit
END
expect<<-END
spawn ssh root@$i "touch /root/.ssh/authorized_keys"
expect "password: "
send "123\n"
expect eof
exit
END
expect<<-END
spawn ssh root@$i "cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys"
expect "password: "
send "123\n"
expect eof
exit
END
done
(4)
#!/bin/sh
CONNECT_MYSQL="mysql -h 192.168.0.123 -uroot -p123456 test_database"
SQL="SELECT * FROM TEST_TABLE"
echo "${SQL}" | ${CONNECT_MYSQL}
(5)
#!/bin/sh
CONNECT_MYSQL="mysql -h 192.168.0.123 -uroot -p123456 test_database"
SQL="SELECT * FROM TEST_TABLE"
echo ${CONNECT_MYSQL} <<MYSQLEOF
${SQL}
MYSQLEOF
read是Linux的一个命令,主要完成用户的交互输入。
参数如下:
-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
-d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志,会举例说 明。
-p 后面跟提示信息,即在输入前打印提示信息。
-e 在输入的时候可以时候命令补全功能。
-n 后跟一个数字,定义输入文本的长度,很实用。
-r 屏蔽,如果没有该选项,则作为一个转义字符,有的话 就是个正常的字符了。
-s 安静模式,在输入字符时不再屏幕上显示,例如login时输入密码。
-t 后面跟秒数,定义输入字符的等待时间。
-u 后面跟fd,从文件描述符中读入。
1. Read的一些选项
Read可以带有-a, -d, -e, -n, -p, -r, -t, 和 -s八个选项。
-a :将内容读入到数值中
echo -n echo
-d :表示delimiter,即定界符,一般情况下是以IFS为参数的间隔,但是通过-d,我们可以定义一直读到出现执行的字符位置。例如read –d madfds value,读到有m的字符的时候就不在继续向后读,例如输入为 hello m,有效值为“hello”,请注意m前面的空格等会被删除。这种方式可以输入多个字符串,例如定义“.”作为结符号等等。
-e :只用于互相交互的脚本,它将readline用于收集输入行。读到这几句话不太明白什么意思,先跳过。
-n :用于限定最多可以有多少字符可以作为有效读入。例如echo –n 4 value1 value2,如果我们试图输入12 34,则只有前面有效的12 3,作为输入,实际上在你输入第4个字符‘3’后,就自动结束输入。这里结果是value为12,value2为3。
-p :用于给出提示符,在前面的例子中我们使用了echo –n “…“来给出提示符,可以使用read –p ‘… my promt?’value的方式只需一个语句来表示。
-r :在参数输入中,我们可以使用’/’表示没有输入完,换行继续输入,如果我们需要行最后的’/’作为有效的字符,可以通过-r来进行。此外在输入字符中,我们希望/n这类特殊字符生效,也应采用-r选项。
-s :对于一些特殊的符号,例如箭头号,不将他们在terminal上打印,例如read –s key,我们按光标,在回车之后,如果我们要求显示,即echo,光标向上,如果不使用-s,在输入的时候,输入处显示^[[A,即在terminal上 打印,之后如果要求echo,光标会上移。
-t :用于表示等待输入的时间,单位为秒,等待时间超过,将继续执行后面的脚本,注意不作为null输入,参数将保留原有的值
2. Read的相关实例
a. 拼接文件
read -u3 i && read -u4 j; echo done 3<afile 4<bfile
b. 输入不在终端显示
Passwd echo
c. 限时输入,否则退出
Number
d. 读取限定字符
Word
e. 等待输出q退出
read -dp -p word
Linux shell之read 用法
#!/bin/bash
#read 用来读取屏幕输入或是读取文件内容。
read -p "please input you name: " name #获取输入变量
read -p "please input you age: " age #获取输入变量
echo "you name is $name ,age is $age" #输出变量内容
#执行结果
[root@sql tmp]# ./read
please input you name: liyang
please input you age: 100
you name is liyang,age is 100
read命令 -n(不换行) -p(提示语句) -n(字符个数) -t(等待时间) -s(不回显)
1、基本读取
read命令接收标准输入(键盘)的输入,或其他文件描述符的输入(后面在说)。得到输入后,read命令将数据放入一个标准变量中。下面是read命令
的最简单形式::
#!/bin/bash
echo -n "Enter your name:" //参数-n的作用是不换行,echo默认是换行
read name //从键盘输入
echo "hello $name,welcome to my program" //显示信息
exit 0 //退出shell程序。
//********************************
由于read命令提供了-p参数,允许在read命令行中直接指定一个提示。
所以上面的脚本可以简写成下面的脚本::
#!/bin/bash
read -p "Enter your name:" name
echo "hello $name, welcome to my program"
exit 0
在上面read后面的变量只有name一个,也可以有多个,这时如果输入多个数据,则第一个数据给第一个变量,第二个数据给第二个变量,如果输入数据个数过多,则最后所有的值都给第一个变量。如果太少输入不会结束。
//*****************************************
在read命令行中也可以不指定变量.如果不指定变量,那么read命令会将接收到的数据放置在环境变量REPLY中。
例如::
read -p "Enter a number"
环境变量REPLY中包含输入的所有数据,可以像使用其他变量一样在shell脚本中使用环境变量REPLY.
2、计时输入.
使用read命令存在着潜在危险。脚本很可能会停下来一直等待用户的输入。如果无论是否输入数据脚本都必须继续执行,那么可以使用-t选项指定一个计时器。
-t选项指定read命令等待输入的秒数。当计时满时,read命令返回一个非零退出状态;
#!/bin/bash
if read -t 5 -p "please enter your name:" name
then
echo "hello $name ,welcome to my script"
else
echo "sorry,too slow"
fi
exit 0
除了输入时间计时,还可以设置read命令计数输入的字符。当输入的字符数目达到预定数目时,自动退出,并将输入的数据赋值给变量。
#!/bin/bash
read -n1 -p "Do you want to continue [Y/N]?" answer
case $answer in
Y | y)
echo "fine ,continue";;
N | n)
echo "ok,good bye";;
*)
echo "error choice";;
esac
exit 0
该例子使用了-n选项,后接数值1,指示read命令只要接受到一个字符就退出。只要按下一个字符进行回答,read命令立即接受输入并将其传给变量。无需按回车键。
3、默读(输入不显示在监视器上)
有时会需要脚本用户输入,但不希望输入的数据显示在监视器上。典型的例子就是输入密码,当然还有很多其他需要隐藏的数据。
-s选项能够使read命令中输入的数据不显示在监视器上(实际上,数据是显示的,只是read命令将文本颜色设置成与背景相同的颜色)。
#!/bin/bash
read -s -p "Enter your password:" pass
echo "your password is $pass"
exit 0
4、读文件
最后,还可以使用read命令读取Linux系统上的文件。
每次调用read命令都会读取文件中的"一行"文本。当文件没有可读的行时,read命令将以非零状态退出。
读取文件的关键是如何将文本中的数据传送给read命令。
最常用的方法是对文件使用cat命令并通过管道将结果直接传送给包含read命令的while命令
例子::
#!/bin/bash
count=1 //赋值语句,不加空格
cat test | while read line //cat 命令的输出作为read命令的输入,read读到的值放在line中
do
echo "Line $count:$line"
count=$[ $count + 1 ] //注意中括号中的空格。
done
echo "finish"
exit 0
cat 150317.txt |while read line
do
echo $line
a=`echo $line | cut -d, -f1`
b=`echo $line | cut -d, -f2`
done
1 #!/bin/bash
2 # "Reading" 变量.
3
4 echo -n "Enter the value of variable 'var1': "
5 # -n 选项, 阻止换行.
6
7 read var1
8 # 注意: 在var1前面没有'$', 因为变量正在被设置.
9
10 echo "var1 = $var1"
11
12
13 echo
14
15 # 一个单独的'read'语句可以设置多个变量.
16 echo -n "Enter the values of variables 'var2' and 'var3' (separated by a space or tab): "
17 read var2 var3
18 echo "var2 = $var2 var3 = $var3"
19 # 如果你只输入了一个值, 那么其他的变量还是处于未设置状态(null).
20
21 exit 0
一个不带变量参数的read命令, 将会把来自键盘的输入存入到专用变量$REPLY中.
例子 11-4. 当使用一个不带变量参数的read命令时, 将会发生什么?
1 #!/bin/bash
2 # read-novar.sh
3
4 echo
5
6 # -------------------------- #
7 echo -n "Enter a value: "
8 read var
9 echo "\"var\" = "$var""
10 # 到这里为止, 都与期望的一样.
11 # -------------------------- #
12
13 echo
14
15 # --------------------------------------------------------------- #
16 echo -n "Enter another value: "
17 read # 没有变量分配给'read'命令, 所以...
18 #+ 输入将分配给默认变量, $REPLY.
19 var="$REPLY"
20 echo "\"var\" = "$var""
21 # 这部分代码和上边的代码等价.
22 # --------------------------------------------------------------- #
23
24 echo
25
26 exit 0
一般的, 当输入给read时, 输入一个\, 然后回车, 将会阻止产生一个新行. -r选项将会让 \ 转义.
例子 11-5. read命令的多行输入
1 #!/bin/bash
2
3 echo
4
5 echo "Enter a string terminated by a \\, then press <ENTER>."
6 echo "Then, enter a second string, and again press <ENTER>."
7 read var1 # 当 read $var1 时, "\" 将会阻止产生新行.
8 # first line 9 # second line
10
11 echo "var1 = $var1"
12 # var1 = first line second line
13
14 # 对于每个以 "\" 结尾的行,
15 #+ 你都会看到一个下一行的提示符, 让你继续向var1输入内容.
16
17 echo; echo
18
19 echo "Enter another string terminated by a \\ , then press <ENTER>."
20 read -r var2 # -r 选项会让 "\" 转义.
21 # first line 22
23 echo "var2 = $var2"
24 # var2 = first line 25
26 # 第一个 <ENTER> 就会结束var2变量的录入.
27
28 echo
29
30 exit 0
read命令有些有趣的选项, 这些选项允许打印出一个提示符, 然后在不输入ENTER的情况下, 可以读入你所按下的字符的内容.
1 # 不敲回车, 读取一个按键字符.
2
3 read -s -n1 -p "Hit a key " keypress
4 echo; echo "Keypress was "\"$keypress\""."
5
6 # -s 选项意味着不打印输入.
7 # -n N 选项意味着只接受N个字符的输入.
8 # -p 选项意味着在读取输入之前打印出后边的提示符.
9
10 # 使用这些选项是有技巧的, 因为你需要用正确的顺序来使用它们.
11
read命令的-n选项也可以检测方向键, 和一些控制按键.
例子 11-6. 检测方向键
1 #!/bin/bash
2 # arrow-detect.sh: 检测方向键, 和一些非打印字符的按键.
3 # 感谢, Sandro Magi, 告诉了我们怎么做到这点.
4
5 # --------------------------------------------
6 # 按键所产生的字符编码.
7 arrowup='\[A'
8 arrowdown='\[B'
9 arrowrt='\[C'
10 arrowleft='\[D'
11 insert='\[2'
12 delete='\[3'
13 # --------------------------------------------
14
15 SUCCESS=0
16 OTHER=65
17
18 echo -n "Press a key... "
19 # 如果不是上边列表所列出的按键, 可能还是需要按回车. (译者注: 因为一般按键是一个字符)
20 read -n3 key # 读取3个字符.
21
22 echo -n "$key" | grep "$arrowup" # 检查输入字符是否匹配.
23 if [ "$?" -eq $SUCCESS ]
24 then
25 echo "Up-arrow key pressed."
26 exit $SUCCESS
27 fi
28
29 echo -n "$key" | grep "$arrowdown"
30 if [ "$?" -eq $SUCCESS ]
31 then
32 echo "Down-arrow key pressed."
33 exit $SUCCESS
34 fi
35
36 echo -n "$key" | grep "$arrowrt"
37 if [ "$?" -eq $SUCCESS ]
38 then
39 echo "Right-arrow key pressed."
40 exit $SUCCESS
41 fi
42
43 echo -n "$key" | grep "$arrowleft"
44 if [ "$?" -eq $SUCCESS ]
45 then
46 echo "Left-arrow key pressed."
47 exit $SUCCESS
48 fi
49
50 echo -n "$key" | grep "$insert"
51 if [ "$?" -eq $SUCCESS ]
52 then
53 echo "\"Insert\" key pressed."
54 exit $SUCCESS
55 fi
56
57 echo -n "$key" | grep "$delete"
58 if [ "$?" -eq $SUCCESS ]
59 then
60 echo "\"Delete\" key pressed."
61 exit $SUCCESS
62 fi
63
64
65 echo " Some other key pressed."
66
67 exit $OTHER
68
69 # 练习:
70 # -----
71 # 1) 使用'case'结构来代替'if'结构,
72 #+ 这样可以简化这个脚本.
73 # 2) 添加 "Home", "End", "PgUp", 和 "PgDn" 这些按键的检查.
?
对于read命令来说, -n选项不会检测ENTER(新行)键.
read命令的-t选项允许时间输入(参考例子 9-4).
read命令也可以从重定向的文件中"读取"变量的值. 如果文件中的内容超过一行, 那么只有第一行被分配到这个变量中. 如果read命令的参数个数超过一个, 那么每个变量都会从文件中取得一个分配的字符串作为变量的值, 这些字符串都是以定义的空白字符来进行分隔的. 小心使用!
例子 11-7. 通过文件重定向来使用read命令
1 #!/bin/bash
2
3 read var1 <data-file
4 echo "var1 = $var1"
5 # var1将会把"data-file"的第一行的全部内容都为它的值.
6
7 read var2 var3 <data-file
8 echo "var2 = $var2 var3 = $var3"
9 # 注意, 这里的"read"命令将会产生一种不直观的行为.
10 # 1) 重新从文件的开头开始读入变量.
11 # 2) 每个变量都设置成了以空白分割的字符串.
12 # 而不是之前的以整行的内容作为变量的值.
13 # 3) 而最后一个变量将会取得第一行剩余的全部部分(译者注: 不管是否以空白分割).
14 # 4) 如果需要赋值的变量个数比文件中第一行以空白分割的字符串个数还多的话,
15 # 那么这些变量将会被赋空值.
16
17 echo "------------------------------------------------"
18
19 # 如何用循环来解决上边所提到的问题:
20 while read line
21 do
22 echo "$line"
23 done <data-file
24 # 感谢, Heiner Steven 指出了这点.
25
26 echo "------------------------------------------------"
27
28 # 使用$IFS(内部域分隔变量)来将每行的输入单独的放到"read"中,
29 # 前提是如果你不想使用默认空白的话.
30
31 echo "List of all users:"
32 OIFS=$IFS; IFS=: # /etc/passwd 使用 ":" 作为域分隔符.
33 while read name passwd uid gid fullname ignore
34 do
35 echo "$name ($fullname)"
36 done </etc/passwd # I/O 重定向.
37 IFS=$OIFS # 恢复原始的$IFS.
38 # 这段代码也是Heiner Steven编写的.
39
40
41
42 # 在循环内部设置$IFS变量,
43 #+ 而不用把原始的$IFS
44 #+ 保存到临时变量中.
45 # 感谢, Dim Segebart, 指出了这点.
46 echo "------------------------------------------------"
47 echo "List of all users:"
48
49 while IFS=: read name passwd uid gid fullname ignore
50 do
51 echo "$name ($fullname)"
52 done </etc/passwd # I/O 重定向.
53
54 echo
55 echo "\$IFS still $IFS"
56
57 exit 0
?
管道输出到read命令中, 使用管道echo输出来设置变量将会失败.
然而, 使用管道cat输出看起来能够正常运行.
1 cat file1 file2 |
2 while read line
3 do
4 echo $line
5 done
但是, 就像Bj鰊 Eriksson所指出的:
例子 11-8. 管道输出到read中的问题
1 #!/bin/sh
2 # readpipe.sh
3 # 这个例子是由Bjon Eriksson所编写的.
4
5 last="(null)"
6 cat $0 | while read line
8 do
9 echo "{$line}"
10 last=$line
11 done
12 printf "\nAll done, last:$last\n"
13
14 exit 0 # 代码结束.
15 # 下边是脚本的(部分)输出.
16 # 'echo'出了多余的大括号.
17
18 #############################################
19
20 ./readpipe.sh
21
22 {#!/bin/sh}
23 {last="(null)"}
24 {cat $0 |}
25 {while read line}
26 {do}
27 {echo "{$line}"}
28 {last=$line}
29 {done}
30 {printf "nAll done, last:$lastn"}
31
32
33 All done, last:(null)
34
35 变量(last)被设置在子shell中, 并没有被设置在外边.
在许多Linux发行版上, gendiff脚本通常都在/usr/bin下, 将find的输出通过管道传到while read结构中.
1 find $1 \( -name "*$2" -o -name ".*$2" \) -print |
2 while read f; do
3 . . .
sed -i "s/`echo -e \\\t`/,/g" /home/123456.txt
sed -i 's/00:00:00//g' /home/123456.txt
awk '{ print "总计," $0}' /home/123456.txt >/home/123.txt
sed -i "1 i `cat /home/123456.txtt`" /home/123.txt