======================================================================
ule-scripts-shell
======================================================================
———————————————————————-
shell02
———————————————————————-
:尚观科技: http://uplooking.com
.. contents::
.. sectnum::
shell中重要的几个内部命令
———————————————————————-
* eval <args> # 把后面的args当作一行命令来执行
* exec <args> # 后面跟命令作为参数, 它不会去创建新的子进程, 也就是说, 它结束后, 脚本就结束了, 不会再执行exec后面的语句了!!
| [nich4@server4 ~]$ cat a.sh
| #!/bin/sh
| #
| echo aaaa
| exec \’ls\’
| echo bbbb
| [nich4@server4 ~]$ chmod +x a.sh
| [nich4@server4 ~]$ ./a.sh
| aaaa
| a.sh
| [nich4@server4 ~]$
* readonly # 定义一个只读变量, 当前shell中不可更改,不可unset
可以先定义变量var, 再执行readonly var
也可以直接执行readonly var=value
* read # 可以一次要求输入多个变量, 由空格分隔
| [root@localhost nich4]# read a b c d e
| 1 2 3 4 5
| [root@localhost nich4]# echo $a $b $c $d $e
| 1 2 3 4 5
| [root@localhost nich4]#
* wait # 使Shell等待在后台启动的所有子进程结束。wait的返回值总是真
* exit # 退出Shell程序。在exit之后可有选择地指定一个数作为返回状态
* shift # 按下面的方式重新命名所有的位置参数变量,即$2成为$1,$3成为$2…在脚本中每使用一次shift语句,都使所有的位置参数依次向左移动一个位置,并使位置参数$#减1,直到减到0为止
* . # 点. 让shell读入指定的脚本文件, 并依次执行那个脚本中的语句, (即把脚本调入到当前shell中来执行)
* : # : 空命令 NOP no op 相当于true,返回0
bash程序的调试
———————————————————————-
在编程过程中难免会出错,有的时候,调试程序比编写程序花费的时间还要多,Shell程序同样如此。
Shell程序的调试主要是利用bash命令解释程序的选择项。调用bash的形式是:
>> bash <参数> file
几个常用的选择项是:
-e 如果一个命令失败就立即退出。
-n 读入命令但是不执行它们。
-u 执行时把未设置的变量看做出错。
-v 当读入Shell命令时把它们显示出来。
-x 执行命令时把命令和它们的参数显示出来。
上面的所有选项也可以在Shell程序内部用\”set -<>\”的形式引用,而\”set +<>\”则将禁止该选择项起作用。如果只想对程序的某一部分使用某些选择项时,则可以将该部分用上面两个语句包围起来。
(1) 未置变量退出和立即退出
未置变量退出特性允许用户对所有变量进行检查,如果引用了一个未赋值的变量就终止Shell程序的执行。Shell通常允许未置变量的使用,在这种情况下,变量的值为空。如果设置了未置变量退出选择项,则一旦使用了未置变量就显示错误信息,并终止程序的运行。未置变量退出选择项为-u。
当Shell运行时,若遇到不存在或不可执行的命令、重定向失败或命令非正常结束等情况时,如果未经重新定向,该出错信息会显示在终端屏幕上,而Shell程序仍将继续执行。要想在错误发生时迫使Shell程序立即结束,可以使用-e选项将Shell程序的执行立即终止。
(2) Shell程序的跟踪
调试Shell程序的主要方法是利用Shell命令解释程序的-v或-x选项来跟踪程序的执行。-v选择项使Shell在执行程序的过程中,把它读入的每一个命令行都显示出来,而-x选择项使Shell在执行程序的过程中把它执行的每一个命令在行首用一个+加上命令名显示出来。并把每一个变量和该变量所取的值也显示出来。因此,它们的主要区别在于:在执行命令行之前无-v,则显示出命令行的原始内容,而有-v时则显示出经过替换后的命令行的内容。
除了使用Shell的-v和-x选择项以外,还可以在Shell程序内部采取一些辅助调试的措施。例如,可以在Shell程序的一些关键地方使用echo命令把必要的信息显示出来,它的作用相当于C语言中的printf语句,这样就可以知道程序运行到什么地方及程序目前的状态。
shell命令替换与扩展
———————————————————————-
* *命令替换* 很简单, 就两种情况: $( ) 和 \\` \\`
* *路径扩展:* 通配符 \\* ? [] {}
.. note::
花括号{}和中括号[]: 可以生成列表(list),其中\\*号以逗号分隔.[]不需要分隔
特殊的: {1..10} # 生成1到10的序列, 类似的还有\”seq 1 10\”
shell参数
———————————————————————-
* 位置参数: 脚本运行时传给脚本的参数 $1, $2 ,… ${10}, ${100}
* 内部参数:
| $# # 参数个数
| $* # 所有参数的字符串
| $@ # 所有参数, 每个参数为一个字符串
| $? # 上一条命令执行后的返回值
| $$ # PID
| $0 # 当前shell的名字
分支语句 流程控制
———————————————————————-
if语句
“““““““““““““““““““““““““““““““““““
::
if [ condition ];then
command
fi
———————
if [ condition ];then
command
else
command
fi
———————
if [ condition ];then
command
elif [ condition ];then
command
else
command
fi
———————-
if [ condition ];thne
command
command
if [ condition ];then
command
fi
else
if [ condition ];then
command
elif [ condition ];then
command
else
command
fi
fi
case语句
“““““““““““““““““““““““““““““““““““
*标准用法:*
::
case \”$arg\” in
arg1 | arg01) # | 或者
cmd
;;
arg2)
cmd
;;
*)
cmd
;;
esac
**eg:**
::
case $1 in
start | begin)
echo \”start\”
;;
stop | end)
echo \”stop\”
;;
\\*)
echo \”Ignorant\”
;;
esac
**eg:** 判断系统
有时候,我们需要写跨平台的脚本, 那么我们就需要判断一下脚本运行在什么平台, 然后再使用相应的方法来实现,如Linux、FreeBSD、Solaris等等::
#!/bin/sh
SYSTEM=`uname -s`
case \”$SYSTEM\” in
Linux)
echo \”My system is Linux\”
echo \”Do Linux stuff here…\”
;;
FreeBSD)
echo \”My system is FreeBSD\”
echo \”Do FreeBSD stuff here…\”
;;
*)
echo \”Unknown system : $SYSTEM\”
echo \”I don\’t what to do…\”
;;
esac
.. note::
case语句的匹配是从上往下的顺序, 因此我们编写的原则也是从上往下
case语句的模板支持匹配: * ? []
* 匹配以n开头的所有情况: n*
* 匹配yes的所有字母大小不同的情况: [yY][eE][sS]
* 但不支持{}匹配,因为可以使用 | 就可以达到目的。
循环语句
———————————————————————-
for循环
“““““““““““““““““““““““““““““““““““
标准用法:
::
for xx in xx xxx xx
do
command
done
for (( i=0 ; i<5 ; i++ )) # 定义一个变量,i<5 循环的退出条件,i++ 代表自加1
do
command;
command;
done
for (( i=30 ; i>0 ; i– )) # 定义一个变量,i<5 循环的退出条件,i++ 代表自加1
do
command;
command;
done
while循环
“““““““““““““““““““““““““““““““““““
实现如果不确定循环的次数,这时候就要用到while
标准用法:
::
while [ condition ] # 只要条件满足就继续循环
do
command
done
until循环
“““““““““““““““““““““““““““““““““““
当满足条件的时候,退出循环, 而while是不满足条件的时候才退出循环.
::
until [ condition ] ; do
command;
command;
…
command;
done
eg:
$ VAR=10
$ until [ \”$VAR\” -eq 0 ]; do
> echo $VAR;
> VAR=$(($VAR-1));
> done
eg:
rannum=`expr $RANDOM % 101`
small=0
big=100
read -p \”Input you guest:\” guest
until [ \”$rannum\” -eq \”$guest\” ] # $rannum = $guest 的时候就退出循环
do
[ … ] && command
done
select
“““““““““““““““““““““““““““““““““““
select 多用来快速的设计一个字符界面的用户交互选择的菜单, 也多与case一起使用.
语法和 for 循环非常相似:
::
select Key in List
do
Command;
done
eg:
#!/bin/sh
#
echo \”请选择:\”
list=\”Success Fail Exit\”
select a in $list;do
if [ \”$a\” = \”Success\” ];then
echo Success
elif [ \”$a\” = \”Fail\” ];then
echo Fail
elif [ \”$a\” = \”Exit\” ];then
exit
else
echo \”No the Choose\”
fi
done
\”奇技淫巧\”
“““““““““““““““““““““““““““““““““““
::
shell_for_loop总结
# Modified: 20101012-11:57:31 by [email protected]
# Filename: loop-example.txt
#!/bin/env bash
#
for i in 1 2 3 4 5
do
echo \”Welcome $i times\”
done
#/bin/env bash
# bash version 3.0+
#
for i in {1..5}
for i in $(seq 1 5)
do
echo \”Welcome $i times\”
done
#!/bin/env bash
# bash version 4.0+
# {START..END..INCREMENT}
#
echo \”Bash version ${BASH_VERSION}…\”
for i in {0..10..2}
do
echo \”Welcome $i times\”
done
#!/bin/env bash
#
for i in `seq 1 2 20`
do
echo \”Welcome $i times\”
done
#!/bin/env bash
# for (( EXP1; EXP2; EXP3 ))
#
for (( c=1; c<=5; c++ ))
do
echo \”Welcome $c times…\”
done
#!/bin/env bash
# infinite loops
#
for (( ; ; )) # other: 1. while true 2. while :
do
echo \”innfinite loops [ hit CTRL+C to stop]\”
done
#!/bin/env bash
#
#for I in 1 2 3 4 5
#do
# statements1 #Executed for all values of \’\’I\’\’, up to a disaster-condition if any.
# statements2
# if (disaster-condition)
# then
# break #Abandon the loop.
# fi
# statements3 #While good and, no disaster-condition.
#done
#
for file in /etc/*
do
if [ \”${file}\” == \”/etc/resolv.conf\” ]
then
countNameservers=`grep -c nameserver /etc/resolv.conf`
echo \”Total ${countNameservers} nameservers defined in ${file}\”
break
fi
done
#!/bin/env bash
#
#for I in 1 2 3 4 5
#do
# statements1 #Executed for all values of \’\’I\’\’, up to a disaster-condition if any.
# statements2
# if (condition)
# then
# continue #Go to next iteration of I in the loop and skip statements3
# fi
# statements3
#done
#
FILES=\”$@\”
for f in $FILES #打印脚本的参数,此处可省略\”in $FILES\”,只需要\”for f\”
do
# if .bak backup file exists, read next file
if [ -f ${f}.bak ]
then
echo \”Skiping $f file…\”
continue
fi
# we are hear means no backup file exists, just use cp command to copy file
/bin/cp $f $f.bak
done
#!/bin/env bash
#
FILE=/etc/passwd
FS=\’:\’
while read line
do
# store field 1
F1=$(echo $line|cut -d$FS -f1)
# store field 2
F2=$(echo $line|cut -d$FS -f6)
# store field
F3=$(echo $line|cut -d$FS -f7)
echo \”User \\\”$F1\\\” home directory is $F2 and login shell is $F3\”
done < $FILE
.. code:: bash
例:
编写脚本,实现一个闹钟
#!/bin/bash
# 定时闹钟
clock=$1
while true
do
now=$(date +%H:%M:%S)
if [ \”$now\” = \”$clock\” ]
then
echo -e \”$now time\’s up !\”
for (( i=0; i<5;i++ ))
do
echo -e -n \”\\a\”
sleep 1
done
break
fi
done
shell变量扩展
———————————————————————-
1. 变量定义与删除
| var_name=value # 等号前后没有空格
| # 字母和数字, 字母开头
| kernel=$(uname -r)
| kernel=`uname -r`
| read var_name
|
| unset var_name
#. 变量引用与显示
| $var_name
| ${var_name}str # 显示指定, 字符串合并
| echo $var_name
#. 变量处理
需要用{}括起来.
* ${var:-default} # 如果变量var没有定义, 则临时使用\”default\”值
| 作用: 如果变量未定义,返回一个默认值
|
| [nich4@server4 ~]# echo ${var}
|
| [nich4@server4 ~]# echo ${var:-default}
| default
| [nich4@server4 ~]# echo ${var}
|
| [nich4@server4 ~]# var=new
| [nich4@server4 ~]# echo ${var:-default}
| new
| [nich4@server4 ~]#
|
* ${var:=default} # 如果变量var没有定义, 则把\”default\”值赋给变量var
| 作用: 如果变量未定义,为变量设初始值,并返回
|
| [nich4@server4 ~]$ echo $var
|
| [nich4@server4 ~]$ echo ${var:=default}
| default
| [nich4@server4 ~]$ echo $var
| default
| [nich4@server4 ~]$
.. note::
变量替换的值也可以使用`(反引号):
echo ${var:-`pwd`}
* ${var:+default} # 如果var已经定义, 且为非空时, 则临时使用\”default\”值
| 作用: 测试一个变量是否存在,如果存在,返回default,不存在返回null
|
| [nich4@server4 ~]$ unset var
| [nich4@server4 ~]$ echo ${var:+default}
|
| [nich4@server4 ~]$ echo $var
|
| [nich4@server4 ~]$ var=old
| [nich4@server4 ~]$ echo ${var:+default}
| default
| [nich4@server4 ~]$ echo $var
| old
| [nich4@server4 ~]$ var=\’\’
| [nich4@server4 ~]$ echo ${var:+default}
|
| [nich4@server4 ~]$ echo $var
|
| [nich4@server4 ~]$
* ${var:?message} # 判断变量var是否定义,且是否非空
| 作用: 捕获未定义变量导致的错误,即如果变量未定义的话,就输出一个错误信息
|
| [nich4@server4 ~]$ var=old
| [nich4@server4 ~]$ echo ${var:?\’Errors!\’}
| old
| [nich4@server4 ~]$ var=\’\’
| [nich4@server4 ~]$ echo ${var:?\’Errors!\’}
| -bash: var: Errors!
| [nich4@server4 ~]$ unset var
| [nich4@server4 ~]$ echo ${var:?\’Errors!\’}
| -bash: var: Errors!
| [nich4@server4 ~]$
|
| 当message没有指定时,shell将显示一条默认的消息:
| [nich4@server4 ~]$ unset var
| [nich4@server4 ~]$ echo ${var:?}
| -bash: var: parameter null or not set
| [nich4@server4 ~]$ var=\’\’
| [nich4@server4 ~]$ echo ${var:?}
| -bash: var: parameter null or not set
| [nich4@server4 ~]$
* ${var:begin:length} # 截取字符串, 从begin开始截取length长度,不指定length的话就是从begin开始到结尾
| 作用: 返回变量值的子串
|
| 0开始记数, 即begin最小为0, 然后从begin开始截取length个字符
|
| [nich4@server4 ~]$ var=\’uplooking.com\’
| [nich4@server4 ~]$ echo ${var:2:7}
| looking
| [root@localhost nich4]# echo ${var:2}
| looking.com
| [nich4@server4 ~]$
* ${#var} # 变量var的值的字符数
| [nich4@server4 ~]$ echo $var
| uplooking.com
| [nich4@server4 ~]$ echo ${#var}
| 13
| [nich4@server4 ~]$
* ${var#pattern} # 去掉变量值开头的与pattern相匹配的部分, 最短匹配模式
* ${var##pattern} # 同上, 但为最长匹配模式
* ${var%pattern} # 去掉变量值结尾的与pattern相匹配的部分, 最短匹配模式
* ${var%%pattern} # 同上, 但为最长匹配模式
* ${var/pattern/str} # 用str替换var中的第一个pattern
* ${var//pattern/str} # 替换var中所有的pattern
| [nich4@server4 ~]$ var=\’/etc/sysconfig/network-scripts/ifcfg-eth0\’
| [nich4@server4 ~]$ echo ${var#*sc}
| onfig/network-scripts/ifcfg-eth0
| [nich4@server4 ~]$ echo ${var##*sc}
| ripts/ifcfg-eth0
|
| [nich4@server4 ~]$ echo $var
| /etc/sysconfig/network-scripts/ifcfg-eth0
| [nich4@server4 ~]$ echo ${var%sc*}
| /etc/sysconfig/network-
| [nich4@server4 ~]$ echo ${var%%sc*}
| /etc/sy
|
| [nich4@server4 ~]$ echo $var
| /etc/sysconfig/network-scripts/ifcfg-eth0
| [nich4@server4 ~]$ echo ${var/sc/XX}
| /etc/syXXonfig/network-scripts/ifcfg-eth0
| [nich4@server4 ~]$ echo ${var//sc/XX}
| /etc/syXXonfig/network-XXripts/ifcfg-eth0
| [nich4@server4 ~]$ echo ${var//sc/} # 删除
| /etc/syonfig/network-ripts/ifcfg-eth0
| [nich4@server4 ~]$
|
| 如果模式以#开头,则必须匹配variable的开头
| 如果模式以%开头,则必须匹配var的结尾
| 如果string为null,匹配部分将被删除
| 如果var为@或*,操作被依次应用于每个位置参数并且扩展为结果列表
算术运算
———————————————————————-
* $(( ))
| [root@localhost nich4]# echo $((4+5))
| 9
| [root@localhost nich4]# echo $((4-5))
| -1
| [root@localhost nich4]# echo $((4*5))
| 20
| [root@localhost nich4]# echo $((4/5))
| 0
| [root@localhost nich4]# echo $((4%5)) # 求余
| 4
| [root@localhost nich4]# echo $((4**5)) # 乘幂
| 1024
| [root@localhost nich4]#
* $[ ]
| [root@localhost nich4]# echo $[4+5]
| 9
| [root@localhost nich4]# echo $[4-5]
| -1
| [root@localhost nich4]# echo $[4*5]
| 20
| [root@localhost nich4]# echo $[4/5]
| 0
| [root@localhost nich4]# echo $[4%5]
| 4
| [root@localhost nich4]# echo $[4**5]
| 1024
| [root@localhost nich4]#
* expr # 外部命令
| [root@localhost nich4]# expr 4 + 5
| 9
| [root@localhost nich4]# expr 4 – 5
| -1
| [root@localhost nich4]# expr 4 / 5
| 0
| [root@localhost nich4]# expr 4 \\* 5
| 20
| [root@localhost nich4]# expr 4 % 5
| 4
| [root@localhost nich4]# expr 4 \\*\\* 5 # 没有乘幂
| expr: syntax error
| [root@localhost nich4]# expr 4 ** 5
| expr: syntax error
| [root@localhost nich4]#
.. note::
运算符两边要有空格! \\*号需要转义!
* let
| [root@localhost nich4]# let var=4+5
| [root@localhost nich4]# echo $var
| 9
| [root@localhost nich4]# let var=4*5
| [root@localhost nich4]# echo $var
| 20
| [root@localhost nich4]# let var=4%5
| [root@localhost nich4]# echo $var
| 4
| [root@localhost nich4]# let var=4-5
| [root@localhost nich4]# echo $var
| -1
| [root@localhost nich4]# let var=4**5
| [root@localhost nich4]# echo $var
| 1024
* bc
| [root@localhost nich4]# echo \”4+5\” | bc
| 9
| [root@localhost nich4]# echo \”4*5\” | bc
| 20
| [root@localhost nich4]# echo \”4-5\” | bc
| -1
| [root@localhost nich4]# echo \”4/5\” | bc
| 0
| [root@localhost nich4]# echo \”4**5\” | bc
| (standard_in) 1: parse error
| [root@localhost nich4]# echo \”4^5\” | bc
| 1024
| [root@localhost nich4]#
.. note::
bc是一个功能强大的计算器! 另外, bc只接受标准输入, 非交互下, 除了使用管道, 我们还可以使用\”here string\”(<<<):
bc <<< 4^5
.. note::
在脚本中我们经常需要递增(加), 下面的方法都是可以的(参考):
i=$[i+1]
$[i++]
$[++i]
i=`expr $i + 1`
i=$(($i+1))
$((i++))
((i++))
let i=i+1
let i++
let $[i++]
i=`bc<<<$i+1`
for ((i=0;i<5;i++)) …
练习作业:
———————————————————————-
1. 把用户指定目录下的所有空文件删除,最后输出删除的文件个数
delempty.sh
#. 使用for循环批量添加19个用户,用户名分别是user01~user19,密码为uplooking。添加后批量删除
addusers.sh delusers.sh
01 02 03 …
1 2 3 …
#. 创建一个/tmp/test目录,里面有3个文件。名字分别为111,222,333。写一个脚本,可以每隔3秒钟循环对这3个文件改名。把111改为222,222改为333,333改为111
rename.sh
#. 使用for循环测试局域网内(192.168.50.0/24)服务器的网络状态(UP|DOWN)
ping_serv.sh
#. 提取出/usr/share/doc目录下的所有的文件名为index.html的文件。把他们集中放在/tmp/index目录中。文件名字按提取顺序更名标记。即:第一个找到的index.html命名为index.html.1。第二个为index.html.2。以此类推
find_index.sh
#. 结合所学知识实现下面这个程序:
实现功能菜单:
执行脚本后
按1,显示当前时间
按2,显示剩余内存
按3,显示在线用户
按4,显示CPU负载
按5,退出脚本
按其他字符,提示超出选择范围后退出
::
————– Menu —————-
1) Show current Time
2) Memory Free
3) Online Users
4) CPU Load
5) Exit
————————————
Enter your choose [1-5]:
转载于:Shell/3251865