| Subcribe via RSS

用Expect实现命令行交互的自动化操作

7月 11th, 2008 | 1 Comment | Posted in shell < by Martian Guo >

Expect 是一个控制交互式程序的工具。你可以用它来编写一段脚本来处理一些交互式的操作,比如执行passwd、su 远程登录 ssh,远程命令操作等 ,如果你有多台服务器需要执行相同的操作命令,用Expect就比较适合。
下面这段文字描述了expect的一些概念。

* Expect 是一种特定的、高级的和通用的编程语言,其语法与 Tcl 相同,并增加了 Tcl 中所没有的一些特殊用途的命令。
    * Expect 是一种可执行程序,从它正确地处理用 Expect 语言编写的输入的角度来看,它实现了这种语言。
    * expect 命令是其中的一个命令,Expect 以此对 Tcl 进行了扩展。
    * Expect 是一个 Tcl 包。一般说来,这意味着任何 Tcl 应用程序都可以在运行时加载 Expect 功能。
    * Expect 是一个基于 C 源代码的库,而这些 C 源代码则深入到 Expect 可加载的包和 Expect 可执行程序。
    * Expect 是某种工具的抽象概念,该工具:
          o 实现终端交互的自动化,甚至在涉及到密码或者其他特殊项目的情况下
          o 实现了一种“对话”模型,通过它对消息和响应的简单规律进行编码

下面两篇文章详细介绍了Expect的使用
http://fanqiang.chinaunix.net/a4/b8/20010912/1000001278.html
http://www.ibm.com/developerworks/cn/aix/library/au-expect/index.html

当然,在学习Expect 之前你应当先学习一些Tcl编程的内容,请看这里 TclTk中文网,另外expect 的安装需要依赖Tcl,你必须首先安装 Tcl。

下面是我写的一段演示脚本,主要操作是从一个文本中批量读取ip地址,普通用户用户名和密码,超级用户密码,执行ssh登录,su root 操作 然后退出

pwd.txt

127.0.0.1       mars    1234        root            123456
192.168.0.3       mars    1234        root            123456

run.exp

#!/usr/bin/expect
set timeout 5
proc do_ssh_login {host username pass susername spass} {
    
set timeout_case 0
    
set done 1
 
    
send_user "\n"
    
spawn ssh $username@$host
    
send_user "正在连接主机 $host\n"
    
while {$done} {
 
    
expect {
            
timeout {
                  
switch -- $timeout_case {
                
0 { send "\n" }
                
1 {
                      
send_user "重试...\n"
                      
send "\n"
                  
}
                
2 {
                    
puts stderr "登录超时...\n"
            
close
            
set done 0
            
break
                  
}
              
}
              
incr timeout_case
              
}
 
        
"*(yes/no)?" {send "yes\n"}       
        
"?assword:"  {send "$pass\n"}
        
"*Permission denied*" {    send_user "登录受限\n"
                    
close
                    
set done 0
                    
break
                      
}
          
"*Connection refused*" { send_user "主机不可访问\n"
                    
close
                    
set done 0
                    
break
                      
}
        
"*$*" {send_user "\n登录成功...\n"
            
if {[string  equal $susername "NULL"] !=1} {
                    
send "export LC_ALL=en_US\r"
                    
send "su - $susername\r"
                      
expect "*Password:*"
                      
sleep 1
                      
send "$spass\n"
                
expect {
                    
"*Authentication failure*" {send_user "密码错误\n"
                                        
close
                                    
break
                                          
}
                          
"*Permission denied*" {send_user "登录受限\n"
                                      
close
                                  
break
                                          
}   
                        
}
            
}
              
}
        
"*#*" {
                
send_user  "超级用户登录成功\n"
                
send_user "\n*************************$host********************************\n"
                
send "cat /proc/cpuinfo |grep model\\ name\n"
                
expect "#"
                
send "df -T\n"
                
expect "#"
                
send "fdisk -l\n"
                
expect "#"
                
send "free\n"
                
expect "#"
                
send "lspci\n"
                
expect "#"
                
expect {
                
"#" {set done 0
                    
send_user "\n***************************************************************\n"
                    
send_user "\n"
                    
send "exit\n"
                  
}
                
}
          
}
        
}
    
}
 
}
 
set f [open "pwd.txt" r]
while { [gets $f line] >= 0 } {
set su [lindex $line 3]
set sp [lindex $line 4]
if { [string length $su] <=0 } {
    
set sp "NULL"
    
set su "NULL"
}
do_ssh_login [lindex $line 0] [lindex $line 1] [lindex $line 2]  $su $sp
}
 
close $f
阅读内文 Tags: , , ,

chroot 环境下让普通用户修改配置文件

6月 26th, 2008 | No Comments | Posted in shell < by Martian Guo >

以前写的一个脚本,因为有一个项目要求让普通用户修改一些配置文件,而不能让他们有root权限。首先想到的是给这个用户sudo 权限用来执行一个脚本调用vi编辑器,而在vi中是可以直接调用系统命令的,因此这样做并不可靠。我想到了在脚本控制下给vi编辑器一个chroot环境,当vi修改完配置文件后,通过脚本控制复制到实际路径中替换掉旧文件并且可以备份每次修改的diff。下面是我写的一个脚本。介绍一下:
首先设置一个vi的chroot环境,主要程序文件,实际vim依赖哪些so可以用命令 ldd vim 来获取到,可以从相应实际目录中拷贝过来。

./
./bin
./bin/vim
./tmp
./etc
./etc/vimrc
./lib
./lib/ld-linux.so.2
./lib/libdl.so.2
./lib/libselinux.so.1
./lib/libgpm.so.1
./lib/libattr.so.1
./lib/libnsl.so.1
./lib/libc.so.6
./lib/libpthread.so.0
./lib/libncurses.so.5
./lib/libresolv.so.2
./lib/libacl.so.1
./lib/libcrypt.so.1
./lib/libperl.so
./lib/libtermcap.so.2
./lib/libm.so.6
./lib/libutil.so.1
./root
./root/.viminfo

下面是脚本,主要功能:自动搜索特定目录下的文件,修改后自动备份和随时回滚的功能,脚本写的很简单,没什么可说的。当然,你肯定又会说了,何必这么麻烦呢,直接把那些配置文件的修改权给这个用户不就好了吗?如果你非要这样也我也没有办法,这里只是提供这样一种方法,到底要怎么实现怎么做要看具体情况。

#!/bin/sh
#################################################
# File edit&backup&restore tools for sudo user                                   #
# martian <martian2008@gmail.com>                                               #
#################################################
if [ "$UID" != 0 ];then
echo access denied
exit  1
fi
chroot="/usr/local/chroot_vim"        # chroot环境路径
backup="/usr/local/configfile_back"  #  备份文件保存目录
path=(/usr/local/tomcat6/conf /usr/local/httpd/conf /usr/local/php/etc)
# 有权限修改配置文件的目录
 
USAGE="Usage: `basename $0` [OPTION]... {FileName} \n  -e Edit File \n  \
-r Restore File
\n"
 
if [ "$#" -eq 0 ] ;then
        
echo -e "${USAGE}"
        
exit 1
fi
get_char()
{
        
SAVEDSTTY=`stty -g`
        
stty -echo
        
stty raw
        
dd if=/dev/tty bs=1 count=1 2> /dev/null
        
stty -raw
        
stty echo
        
stty $SAVEDSTTY
}
 
answer()
{
        
ANSWER=""
        
while [[ "$ANSWER" != "y" ]] && [[  "$ANSWER" != "Y" ]]
        
do
        
echo -en "Do you want to "
        
echo -en "\\033[0;35m"
        
echo -en "$info"
        
echo -en "\\033[0;38m"
        
echo " this File? (y/n)"
        
read ANSWER
        
case $ANSWER in
              
(y|Y) return 0 ;;
              
(n|N) return 1 ;;
               *)
continue;
        
esac
        
done
}
select_diff()
{
        
N=""
        
read N
        
until [[ $N =~ ^[0-9]+$ ]]   # bash 3.0
        
do
        
echo -n "Please type one number:"
        
read N
        
done
}
 
while getopts e:rOPTION;
do
        
case "$OPTION" in
        
e) info="Edit"
          
F="$OPTARG";;
        
r) info="Restore"
          
F="$OPTARG";;
        \?)
echo -e $USEAGE
            
exit 1
        
esac
done
if [ -n "$F" ];then
for filedir in ${path[*]}
do
 
for f in `/usr/bin/find "$filedir" -type f -name  "$F"`
 
do
        
echo
        
echo -en "\\033[0;35m"
        
echo -en "$info"
        
echo -en "\\033[0;38m"
        
echo -en " File "
        
echo -en "\\033[0;36m"
        
echo "$f ..."
        
echo -en "\\033[0;38m"
        
answer
        
if [ "$?" == 0 ];then
          
filename="`echo "$f" |sed 's#/#\|#g'`"
          
if [ "$info" == "Restore" ];then
                
f1=(`/usr/bin/find "$backup"/"$filename".diff.* -xdev 2>/dev/null `)
                
echo -ne "Found "
                
echo -ne "\\033[0;35m"
                
echo -ne ${#f1[*]}
                
echo -ne "\\033[0;38m"
                
echo " Diff File"
                
if [ "${#f1[*]}" == 0 ];then
                  
echo "Skip..."
                
else
                  
i=${#f1[*]}
                  
for f4 in ${f1[*]}
                  
do
                        
echo -ne "`/bin/basename $f4` "
                        
echo `ls -l "$f4" |awk '{print $6, $7, $8}'`
                  
done
                  
echo -n "Please type one number to Restore [ ${#f1[*]}-1 ]:"
                  
select_diff
                  
echo -ne "Your Select "
                  
echo -ne "\\033[0;35m"
                  
echo -ne "$N"
                  
echo -ne "\\033[0;38m"
                  
echo " to Restore"
                  
pushd "`/usr/bin/dirname $f`" >/dev/null
                  
until [ $N -gt $i ]
                  
do
 
                        
i=$((i-1))
                        
echo "Process File ${f1[$i]}......"
                      
/usr/bin/patch  <${f1[$i]}
                        
if [ "$?" == 0 ];then
                      
/bin/rm ${f1[$i]}
                        
fi
                  
done
                  
popd >/dev/null 2>&1
                
fi
        
else
      
/bin/cp -a "$f" "$chroot"/tmp/"$filename"
      
/usr/sbin/chroot "$chroot" /bin/vim /tmp/"$filename"
      
/usr/bin/diff -Nu  "$chroot"/tmp/"$filename"  "$f"  \
                                >
"$chroot"/tmp/"$filename".diff
        
if [ "$?" != 0 ];then
                
echo -n "Backup Confilg File..."
                
f2="$backup"/"$filename".diff
                
f1=(`/usr/bin/find $f2.* -xdev 2>/dev/null `)
                
i=${#f1[*]}
                
i=$((i+1))
                
echo $i
              
/bin/mv "$chroot"/tmp/"$filename".diff "$f2".$i
                
echo done
                
echo -n "Saveing... Press any key to continue..."
              
/bin/cp -a "$chroot"/tmp/"$filename" "$f"
                
echo done
                
char=`get_char`
        
fi
        
fi
    
fi
 
done
done
fi
阅读内文 Tags: , ,

用awk过滤查看netstat 连接数

6月 23rd, 2008 | 3 Comments | Posted in shell < by Martian Guo >
netstat -an | awk '/^tcp/{ A[$NF]++} END{ for (a in A) print a, A[a]}'
netstat -an |awk '/LISTEN/{next};/^tcp/{s=split($5, N,":"); \
A[N[s-1]]++} END{ for (a in A) print a, A[a]}
'
阅读内文 Tags: ,

Bash shell 中模拟多线程

6月 23rd, 2008 | No Comments | Posted in shell < by Martian Guo >
#!/bin/bash
 
SEND_THREAD_NUM=13
tmp_fifofile="/tmp/$$.fifo"                  # 脚本运行的当前进程ID号作为文件名
mkfifo "$tmp_fifofile"                          # 新建一个随机fifo管道文件
exec 6<>"$tmp_fifofile"                      # 定义文件描述符6指向这个fifo管道文件
rm $tmp_fifofile
for ((i=0;i<$SEND_THREAD_NUM;i++));do
        
echo                                          # for循环 往 fifo管道文件中写入13个空行
done >&6
 
for i in `seq 100`;do                            # 100 次 for 循环 开始
        
read -u6                                    # 从文件描述符6中读取行(实际指向fifo管道)
 
        
{
                
echo $i                         # 打印 i
                
sleep 3                         # 暂停3秒
                
echo >&6                    # 再次往fifo管道文件中写入一个空行。
        
} &
 
# {} 这部分语句被放入后台作为一个子进程执行,所以不必每次等待3秒后执行
#下一个,这部分的echo $i几乎是同时完成的,当fifo中13个空行读完后 for循环
# 继续等待 read 中读取fifo数据,当后台的13个子进程等待3秒后,按次序
# 排队往fifo输入空行,这样fifo中又有了数据,for语句继续执行
 
        
pid=$!                                   #打印最后一个进入后台的子进程id               
      
echo $pid
 
done
wait
exec 6>&-                                    #删除文件描述符6
 
 
exit 0
阅读内文

awk 思维导图 快速学习

6月 21st, 2008 | 2 Comments | Posted in shell < by Martian Guo >

制作软件:FreeMind 0.9.0

源文件下载

刚做好的思维导图,新鲜出炉!不过相对于awk软件来说,那它可是历史悠久了,希望这张图能对学习awk的朋友有所帮助吧,如有不对或者遗漏的地方望请指正,当然不可能这个图能包含awk的一切,但已经是相当丰富了。如果你实在是记不住这张图上的内容,可以把它打印出来贴在墙上,前提是你家有足够大的墙壁来贴。

阅读内文 Tags: ,

sed 思维导图 快速学习指南

6月 18th, 2008 | No Comments | Posted in Linux, shell < by Martian Guo >

Flash plugin or Javascript are turned off. Activate both and reload to view the mindmap

sed mind map

阅读内文 Tags: ,