首页 > 代码库 > bash脚本编基础
bash脚本编基础
使用多个命令(;):
如果需要两个或多个命令一起执行,用分号把这些命令隔开;
#date ; ifconfig eth0
Sat Nov 1 08:47:46 CST 2014
eth0 Link encap:Ethernet HWaddr 00:50:56:9F:22:36
inet addr:192.168.57.23 Bcast:192.168.57.255 Mask:255.255.255.0
inet6 addr: fe80::250:56ff:fe9f:2236/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1785133 errors:13 dropped:13 overruns:0 frame:0
TX packets:1707231 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:245612681 (234.2 MiB) TX bytes:1915997555 (1.7 GiB)
Interrupt:18 Base address:0x2000
2. 显示文本(echo):
echo的命令格式:
echo [option] [STRING]
option:
-n: 不打印换行符
-e: 启用转义符
-E:禁用转义符(缺省设置)
-e选项可以使用的转义字符:
\\: 反斜杠
\a: 响铃
\b: 退格键
\n: 换行符
\t: 水平制表符
\v: 垂直制表符
\0NNN:
\033[##n:
第一个#:3表示前景色
4表示背景色
第二个#:颜色,1-7
\033[0m:控制符结束
#echo -e "\033[31mHello World\033[0m"
Hello World
3. 变量
变量的类型:
本地变量: 只对当前shell有效,对其子shell以及其它shell都无效;
定义变量:[set] Var_Name="Value"
引用变量: $Var_Name
撤消变量: unset Var_Name
局部变量:仅对局部代码有效
local Var_Name="Value"
环境变量:
全局环境变量:对当前shell及其子shell有效;
printenv: 查看全局环境变量
export Var_Name="Value" 设置全局环境变量
局部环境变量:只对创建它们的shell有效;
set: 查看局部环境变量;
Var_Name="Value" 设置局部环境变量
PS1: 控制默认命令提示符的格式
PS2: 控制后续命令行提示符的格式
echo $PS1
[\u@\h \W]\$
echo $PS2
>
在提示符中使用的特殊字符总结:
\a: 报警字符
\d: "日月年"格式显示的日期
\e: ASCII转义字符
\h: 本地主机名
\H: 完全限定域名(FQDN)
\j: shell当前管理的任务数
\l: shell终端设备中的基名
\n: ASCII换行符
\r: ASCII回车符
\t: 24小时制HH:MM:SS格式的当前时间
\T: 12小时制的当前时间
\@: 12小时制am/pm格式的当前时间
\u: 当前用户的用户名
\v: bash shell的版本
\V: bash shell的发行版本
\w: 当前工作目录
\W: 当前工作目录的基名
\!: 这个命令在bash shell历史记录中的位置
\#: 这个命令在当前命令行的位置
\$: 普通用户下的$,root用户下的#
\nnn: 与八进制数nnn对应的字符
\\: 反斜线\
\[: 开始一个控制字符序列
\]: 结束一个控制字符序列
位置变量:
$1 $2 ... $n
./first.sh 1 2
$1:1
$2:2
特殊变量:
$0: 脚本名称自身
$?: 上一条命令的执行状态;
状态用数字来表示: 0 - 255
0: 表示命令执行成功
1-255:表示命令执行失败
$#: 位置参数的个数
$@:引用所有的位置参数
$*:引用所有的位置参数
4. 反引号(``):用于引用命令命令执行的结果
#date_now=`date`
#echo $date_now
Sat Nov 1 09:27:04 CST 2014
5. 输入输出重定向
标准输入:stdin-->0
标准输出:stdout-->1
标准错误: stderr-->2
输入重定向:
<: 输入重定向:
<< EOF:用于在脚本中创建文件或生成菜单
输出重定向:
>: 覆盖输出
>>: 追加输出
set -C: 禁止使用覆盖重定向至已经存在的文件;
set +C: 关闭上术特性
>|: 在-C特性下,强制使用覆盖重定向
同时重定向标准输出和错误输出:
COMMAND > /path/to/outfile 2> /path/to/errfile
COMMAND &> /path/to/somefile
COMMAND > /path/to/somefile 2> &1
在脚本中重定向:
临时重定向
#!/bin/bash
echo "this is an error" >&2
echo "this is an output."
永久重定向
#!/bin/bash
exec 1>testout
echo "This is test of redrecting all output."
echo "from a script to another file."
把所有的标准输出重定向到文件testout;
#!/bin/bash
exec 2>testerror
yy
重定向所有的标准错误重定向到文件testerror
在脚本中重定向输入
exec 0<testfile
从文件testfile中获取输入,而不是从0(标准输入);
#!/bin/bash
exec 0<testfile
count=1
while read line; do
echo "Line#$count:$line"
let count+=1
done
注:read命令用于读取用户在键盘上的标准输入数据,将stdin重定向到文件后,read会到文件去读取数据,而不是stdin.
创建自定义的重定向
除了0 1 2三个默认的文件描述符(FD)之外,还有3-8几个可以使用的文件描述符
创建输出文件描述符
#!/bin/bash
exec 3>test13out
echo "this should display on the monitor"
echo "this should be stored in the file" >&3
echo "this should display on the monitor"
注:exec将fd 3重定向到文件test13out,所以重定向到&3的文件将被写入test13out
6. 管道(COMMAND1 | COMMAND2):前一个命令的输出作为后一个命令的输入
#ifconfig eth0 | grep "inet addr"
inet addr:192.168.57.23 Bcast:192.168.57.255 Mask:255.255.255.0
7. 执行算术运算
执行算术运算的四种方式:
let varName=算术表达式
varName=$[算术表达式]
varName=$((算术表达式))
varName=`expr $num1 + num2`
编写脚本:test.sh
#!/bin/bash
let varName1=1+1
varName2=$[1+2]
varName3=$((1+3))
varName4=`expr 1 + 4`
echo "varName1:$varName1"
echo "varName2:$varName2"
echo "varName3:$varName3"
echo "varName4:$varName4"
#sh test.sh
varName1:2
varName2:3
varName3:4
varName4:5
8. 脚本退出状态码
变量$?用于保存最后一条命令退出的状态码,默认情况下,一个成功结束的命令的退出码为0,也可以在脚本中自定义退出码:
exit num(num为保留值之外的数字)
num: 0 - 255
0: 命令成功结束
1:通用未知错误
2:误用shell命令
126:命令不可执行
127: 没找到命令
128: 无效退出参数
128+x: Linux信号x的严重错误
130: 命令通过Ctrl + C终止
255: 退出状态码越界
9. 脚本的逻辑控制
if 语句:
单分支:
if 条件;then
分支1;
fi
双分支:
if 条件;then
分支1;
else
分支2;
fi
多分支:
if 条件;then
分支1;
elif 条件2;then
分支2;
elif 条件3;then
分支3;
...
else
分支n;
fi
例:
if [ ! -d /ccdb ]; then
mkdir /ccdb
fi
for 循环语句:
for varName in 列表;do
循环体
done
关于列表读取:
1.直接从列表中读取,比如:for varName in /etc/sysctl.conf /etc/passwd /etc/group
2.从变量中读取,比如:for varName in $varName
3.从命令中读取,比如:for varName in `cat /etc/passwd`
4.从通配符读取目录,比如:for varName in /etc/init.d/*
C语言风格的for格式:
for (( variable assignment; condition; iteration process ))
比如:
for (( i=1; i<=10; i++))
do
echo "The next number is $i"
done
例1:
for fileName in /etc/{passwd,shadow,group}; do
echo $fileName
done
例2:
for fileName in /etc/*; do
echo $fileName
done
例3:
for fileName in `cat /etc/passwd`; do
echo $fileName
done
例4:
varName=`cat /etc/passwd`
for fileName in $varName; do
echo $fileName
done
例5:
for i in {1..10}; do
echo $i
done
while 循环语句:
while 条件测试;do
循环体
done
当条件测试为真(测试命令返回的是退出状态码0)时,一直循环,直到退出状态码为非0
例:
#!/bin/bash
i=0
while [ $i -le 10 ]; do #i小于10时,执行循环;当i大于10时,循环结束。
let i++
echo $i
done
until 循环语句:
until 条件测试;do
循环体;do
done
当条件测试退出状态码为非0时执行循环,一旦测试命令退出状态码为0,循环结束
例:
#!/bin/bash
i=0
while [ $i -ge 10 ]; do #当i小于10时,执行循环;当i大于10时,循环结束。
let i++
echo $i
done
case语句的语法格式:
case用于替代if-then-else的简便形式
case expression in
pattern1)
suite1
;;
pattern2)
suite2
;;
...
patternn)
suiten
;;
*)
other_suite
;;
esac
例:
#!/bin/bash
#
myService=`basename $0`
lockFile="/var/lock/subsys/$myService"
[ $# -lt 1 ] && echo "Usage: $myService {start|stop|restart|status}" && exit 4
case $1 in
‘start‘)
touch $lockFile
echo "Starting $myService OK"
;;
‘stop‘)
rm -f $lockFile
echo "Stopping $myService OK"
;;
‘restart‘)
rm -f $lockFile
touch $lockFile
echo "Restarting $myService OK"
;;
‘status‘)
if [ -f $lockFile ]; then
echo "$myService is running"
else
echo "$myService is stopped"
fi
;;
*)
echo "Usage: $myService {start|stop|restart|status}"
exit 3
;;
esac
10. 条件测试
条件判断的常用类型:
整数测试
字符测试
文件测试
逻辑运算:
与运算:表达式1 && 表达式2
或运算:表达式1 || 表达式2
非运算:! 表达式
bash中如何做测试:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
整数测试:
二元测试:
num1 OPRAND num2
-gt: 大于
-lt: 小于
-ge: 大于等于
-le: 小于等于
-ne: 不等于
-eq: 等于
例:
#/bin/bash
sum=0
i=1
while [ $i -le 100 ]; do
let sum+=$i
let i++
done
echo $sum
字符测试:
单目:
-n $stringVar: 字符串是否为空,不空为真,空为假;
-z $stringVar: 字符串是否为空,空为真,不空为假;
例:
#/bin/bash
s="hello"
if [ -n $s]; then
echo "false"
fi
双目:
!=: 不等于
>: 大于
<: 小于
==: 等于,等值比较
=~左侧是字符串,右侧是模式,判定左侧的字符串能否被右侧的模式所匹配,通常在[[]]中使用;
例:
#!/bin/bash
#
s="hello world"
[[ $s =~ "hello" ]] && echo "success"
文件测试:
单目:
-b file: 测试file是否存在且是否是block设备
-c file: 测试file是否存在且是否是字符设备
-d file: 测试file是否存在且是目录
-e file: 测试file是否存在
-f file: 测试文件是否存在且是一个普通文件
-g file: FILE exists and is set-group-ID
-G file: FILE exists and is owned by the effective group ID
-h file: 测试file是否存在且是一个链接文件和-L功能一样
-L file:
-O file: FILE exists and is owned by the effective user ID
-p file: 测试file是否存在且是一个管道文件
-r file: 测试file是否存在且有读权限
-s file: 测试file是否存在,文件size大于0
-S file: 测试file是否存在且是一个sock文件
-t file: 测试文件文件描述符是否被一个终端打开
-u file: FILE exists and its set-user-ID bit is set
-w file: 测试file是否存在且有写权限
-x file: 测试文件是否存在且有执行权限
例:
#!/bin/bash
if [ ! -d /backup ]; then
mkdir /backup
fi
双目:
FILE1 -ef FILE2:FILE1 and FILE2 have the same device and inode numbers
FILE1 -nt FILE2:FILE1 is newer (modification date) than FILE2
FILE1 -ot FILE2:FILE1 is older than FILE2
例:
#!/bin/bash
touch /tmp/a.txt
ln -s /tmp/a.txt /tmp/b.txt
fileName1=/tmp/a.txt
fileName2=/tmp/b.txt
[ $fileName1 -ef $fileName2 ] && echo "they are the same file."
bash编程之组合测试条件深入探讨:
逻辑与:多个条件同时满足
[ CONDITION1 ] && [ CONDITION2 ]
[ CONDITION1 -a CONDITION2 ]
[[ CONDITION1 && CONDITION2 ]]
注意:前两个使用单或双中括号都可,但,&&不允许用于单中括号中,所以第三种只能用于双中括号中;
逻辑或:多个条件中有一个满足即为真;
[ CONDITION1 ] || [ CONDITION2 ]
[ CONDITION1 -o CONDITION2 ]
[[ CONDITION1 || CONDITION2 ]]
注意:||不允许用于单中括号中;
例:脚本1
#!/bin/bash
if [ -d /backup ] && [ -f /backup/test.txt ]; then
echo "This file exist!"
fi
例:脚本2
#!/bin/bash
if [[ -d /backup && -f /backup/test.txt ]]; then
echo "This file exist!"
fi
脚本1和脚本2的效果完全一样。
if-then的高级特性
使用双尖括号(( expression ))
术语expression可以是任意的数学赋值或比较表达式
双尖括号命令符号:
var++: 后增
var--: 后减
++var: 先增
--var: 先减
!: 逻辑求反
~: 位求反
**: 求幂
<<: 左位移
>>: 右位移
&: 位布尔和
|: 位布尔或
&&: 逻辑和
||: 逻辑或
例:
#!/bin/bash
val1=10
if (( $val1 ** 2 > 90 )); then
(( val2 = $val1 ** 2 ))
echo "The square of $val1 is $val2"
fi
使用双方括号[[ expression ]]
双方括号里的expression使用了test命令中采用的标准字符串进行比较。但它提供了test命令未提供的另一个特性(模式匹配)
例:
#!/bin/bash
if [[ $USER == r* ]]; then
echo "Hello $USER"
else
echo "Sorry,I do not know you!"
fi
11. 处理用户输入
位置参数:$1 $2 ... $n
例:test2.sh
#!/bin/bash
[ $# -ne 2 ] && echo "Usage: `basename $0` num1 num2"
let total=$1 * $2
echo "The first parameter is $1."
echo "The second paramter is $2."
echo "The total value is $total."
echo "This script name is `basename $0`
#sh test3.sh 2 5
The first parameter is 2.
The second paramter is 5.
The total value is 10.
This script name is test3.sh.
$* 和 $@:用于提取所有位置参数
例: 脚本test4.sh
#!/bin/bash
count=1
for param in "$*"; do
echo "\$* parameter #$count = $param"
count=$[$count+1]
done
count=1
for param in "$@"; do
echo "\$@ parameter #$count = $param"
count=$[$count+1]
done
#sh test4.sh apple banana juice pea
$* parameter #1 = apple banana juice pea
$@ parameter #1 = apple
$@ parameter #2 = banana
$@ parameter #3 = juice
$@ parameter #4 = pea
变量移动:
shift:
默认情况下,shift会将每个参数变量减1;变量$3的值会移到$2,变量$2的值会移到$1,而变量$1的值会被删除。
例1:
#!/bin/bash
sum=0
for i in `seq 1 $#`; do
let sum+=$1
shift
done
echo $sum
例2:
#!/bin/bash
#
[ $# -lt 2 ] && echo "Too less argements, quit" && exit 3
if [[ "$1" == "-u" ]];then
userName="$2"
shift 2
fi
if [ $# -ge 2 ] && [ "$1" == "-v" ]; then
verFlag=$2
fi
verFlag=${verFlag:-0}
if [ -n $verFlag ];then
if ! [[ $verFlag =~ [012] ]];then
echo "Wrong parameter."
echo "Usage: `basename $0` -u UserName -v {1|2}"
exit 4
fi
fi
# echo $userName $verFlag
if [ $verFlag -eq 1 ]; then
grep "^$userName" /etc/passwd | cut -d: -f1,3,4,6
elif [ $verFlag -eq 2 ];then
grep "^$userName" /etc/passwd | cut -d: -f1,3,4,6,7
else
grep "^$userName" /etc/passwd | cut -d: -f1,3,4
fi
本文出自 “虎虎生威” 博客,请务必保留此出处http://tobeone.blog.51cto.com/817917/1542466