您的位置:首页 > 运维架构 > Shell

bash脚本编程语法知识点总结

2014-08-03 10:57 471 查看
bash中for循环语句:适用于循环变量已知的场景
for 循环语句格式:
for VAR_NAME in LIST;do
循环体
done
其中list列表中包含一个或多个元素,列表的生成方式:
1、手动给定列表,如:for i in 1 2 3 4 5;do列表元素之间用空格隔开
2、数值列表:{1..100}、`seq 1 100`;
3、$*,$@,使用用户提供的参数列表作为循环列表
4、使用命令生成列表。
注:for循环的list是以空格分开的多个元素
示例:
求100以内所有整数的和(循环列表是数值列表的方式)
#!/bin/bash
#
sum=0
for i in {1..100};do
sum=$[$sum+$i]
done
echo $sum
求100以内奇数的和(循环列表由数值列表生成)
#!/bin/bash
#
sum=0
for i in `seq{1,2,100}`;do
sum=$[$sum+$i]
done
echo $sum
求当前系统上所有用户的ID之和(循环列表由命令生成)
#!/bin/bash
#
sum=0
for i in `cut -d: -f3 /etc/passwd`;do
sum=$[$sum+$i]
done
echo "user id sum is $sum"
分别统计/etc/rc.d/rc.sysinit、/etc/rc.d/init.d/functions和/etc/inittab文件中以#开头的行的行数和空白行数;(循环列表手动指定)
#!/bin/bash
#
for i in /etc/rc.d/rc.sysinit /etc/rc.d/init.d/functions /etc/inittab ;do
line1=`grep "^#" $i | wc -l | cut -d' ' -f1`
line2=`grep "^$" $i | wc -l | cut -d' ' -f1`
echo "$i has # lines $line1.blank lines $line2."
done
bash算数运算
在bash中变量属于弱类型变量,可以随时声明使用,默认情况下数值类型的变量也会被理解为字符保存,如果没有提前定义整型变量,字符型的数字依然可以参与算术运算,bash会执行变量类型的隐式转换,若定义整型变量,可以使用let和declare声明变量:
let VAR_NAME=INTEGER_VALUE,如let a=3
declare -i VAR_NAME=INTEGER_VALUE,如declare -i a=3
bash中实现算术运算的方式如下:
let VAR_NAME=ARITHMATIC_EXPRESSION
VAR_NAME=$[ARITHMATIC_EXPRESSION]
VAR_NAME=$((EXPRESSION))
VAR_NAME=$(expr $num1 + $num2)
bash中常用的算术运算符有:+、-、*、/、%、**
示例:
求100以内所有整数的和(循环列表是数值列表的方式)
#!/bin/bash
#
sum=0
for i in {1..100};do
sum=$[$sum+$i]
done
echo $sum
bash中的逻辑运算
常见的逻辑运算有:与、或、非、异或
逻辑运算的运算法则:
与运算:
真,假:
真 && 真 = 真
真 && 假 = 假
假 && 真 = 假
假 && 假 = 假
或运算:
真,假
真 || 真 = 真
真 || 假 = 真
假 || 真 = 真
假 || 假 = 假
非运算:
非运算即是对操作数进行取反操作
!真 = 假
!假 = 真
异或运算:
真 XOR 真 = 假
假 XOR 假 = 假
真 XOR 假 = 真
假 XOR 真 = 真
bash中的条件测试
执行条件测试的语法格式:
test EXPR
[ EXPR ]
` EXPR `
根据比较时的操作数的类型,将测试类型分为以下几种:
整型测试:整数比较
字符测试:字符串比较
文件测试:判断文件的存在性及属性等
注:比较运算通常只在同一类型的数值之间进行比较
整型测试的操作符:-gt(大于)、-lt(小于)、-ge(大于等于)、-le(小于等于)、-eq(等于)、-ne(不等于)
字符串测试的操作符分为两种:
双目操作符:>(大于)、<(小于)、==(等于)、!=(不等于)、=~(字符串匹配)
增强型赋值操作符:+=, -=, *=, /=, %=
单目:-n String: 是否不空,不空则为真,空则为假
-z String: 是否为空,空则为真,不空则假
示例
如果当前主机的主机名为localhost,则将其修改为www.magedu.com
#!/bin/bash
#
if [ `hostname` == 'localhost' ]; then
hostname www.magedu.com
fi
bash中的选择语句if
在bash中if语句有三种格式:
1、单分支if语句,语法格式如下:
单分支的if语句:
if 测试条件; then
选择分支
fi
当测试条件满足时,执行选择分支语句
2、双分支的if语句:
if 测试条件; then
选择分支1
else
选择分支2
fi
当测试条件满足时,执行选择分支1,不满足时,执行选择分支2
示例:
通过命令行给定一个文件路径,而后判断:如果此文件中存在空白行,则显示其空白行的总数;否则,则显示无空白行;
#!/bin/bash
#
if grep "^[[:space]]*$" $1 &> /dev/null; then
echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines."
else
echo "No blank lines"
fi
3、多分支的if语句:
if 条件1; then
分支1
elif 条件2; then
分支2
elif 条件3; then
分支3
...
else
分支n
fi
示例:
传递一个用户名给脚本: 如果此用户的id号为0,则显示说这是管理员如果此用户的id号大于等于500,则显示说这是普通用户,否则,则说这是系统用户;
#!/bin/bash
#
if [ $# -lt 1 ]; then
echo "Usage: `basename $0` username"
exit 1
fi
if ! id -u $1 &> /dev/null; then
echo "Usage: `basename $0` username"
echo "No this user $1."
exit 2
fi
if [ $(id -u $1) -eq 0 ]; then
echo "Admin"
elif [ $(id -u $1) -ge 500 ]; then
echo "Common user."
else
echo "System user."
fi

bash条件测试之文件测试:
-a FILE:文件存在为真,不存在为假
-e FILE:文件存在为真,不存在为假
-f FILE:文件存在且为普通文件为真,否则为假
-d FILE:文件存在且为目录为真,否则为假
-L/-h FILE:存在并且为符号链接文件,则为真,否则为假
-b:块设备
-c:字符设备
-S:套接字文件
-p:命名管道
-s FILE:存在并且为非空文件则为真,否则为假,文件大小不为0
-r FILE:文件有读取权限为真,否则为假
-w FILE:文件有写入权限为真,否则为假
-x FILE:文件有执行权限为真,否则为假
-N FILE:文件的修改时间要新于访问时间,mtime newer than atime
file1 -nt file2:file1的mtime新于file2则为真,否则为假;
file1 -ot file2:file1的mtime旧于file2则为真,否则为假;
例如:如果wget命令对应的可执行文件存在且可执行,则使用它下载http://172.16.0.1/centos6.5.repo至当前目录中;
#!/bin/bash
#
downURL='http://172.16.0.1/centos6.5.repo'
downloader=`which wget`
if [ -x $downloader ]; then
$downloader $downURL
fi
写一个脚本,完成如下任务:
1、分别复制/var/log下的文件至/tmp/logs/目录中;
2、复制目录时,才使用cp -r
3、复制文件时,使用cp
4、复制链接文件,使用cp -d
5、余下的类型,使用cp -a
#!/bin/bash
#
if [ ! -d /tmp/logs ];then
mkdir /tmp/logs
fi
for file in `ls /var/log`;do
if [ -d /var/log/$file ];then
cp -r /var/log/$file /tmp/logs
elif [ -L /var/log/$file ];then
cp -d /var/log/$file /tmp/logs
elif [ -f /var/log/$file ];then
cp /var/log/$file /tmp/logs
else
cp -a /var/log/$file /tmp/logs
fi
done

bash编程之交互编程
我们编写的脚本有时候需要与用户交互,提示用户输入必要的数据,以便脚本执行下一步的操作,我们可以使用read命令显示一段信息,提示用户键入我们需要的参数
同时,我们还可以指定等待用户输入超时时间,如果超过指定时间,用户未输入,则自动退出等。
read命令的语法格式:
read -p "PROMPT" VAR
read -t # -p "PROMPT" VAR
示例:
输入用户名,可返回其shell
#!/bin/bash
#
read -p "Plz input a username: " userName
if id $userName &> /dev/null; then
echo "The shell of $userName is `grep "^$userName\>" /etc/passwd | cut -d: -f7`."
else
echo "No such user. stupid."
fi
显示一个如下菜单给用户:
cpu) show cpu infomation
mem) show memory infomation
*) quit
1、如果用户选择了cpu,则显示/proc/cpuinfo文件的内容;
2、如果用户选择了mem,则显示/proc/meminfo文件的内容;
3、退出
#!/bin/bash
#
echo "---------menu----------"
echo "cpu) show cpu infomation"
echo "mem) show memory infomation"
echo "*) quit"
echo "-------menu------------"
read -p "Plz give your choice: " choice
if [ "$choice" == 'cpu' ]; then
cat /proc/cpuinfo
elif [ "$choice" == 'mem' ]; then
cat /proc/meminfo
else
echo "Quit"
exit 3
fi

字串测试中的模式匹配
有些情况下我们的条件测试中需要使用模糊匹配的功能,比如一个字串是否在一个比它长度大的字符串中存在的情况下,为了达到通配的效果,而不是精确匹配的情况下,我们需要使用到字串测试的模式匹配功能,其语法格式如下:[[ "$var" =~ PATTERN ]]
示例:
例如:让用户给定一个用户名,判断其是否拥有可登录shell;
/bin/sh, /bin/bash, /bin/zsh, /bin/tcsh, /sbin/nologin, /sbin/shutdown

#!/bin/bash
#
read -p "Plz input a username: " userName
userInfo=`grep "^$userName\>" /etc/passwd`
if [[ "$userInfo" =~ /bin/.*sh$ ]]; then
echo "can login"
else
echo "cannot login"
fi

bash编程之循环:while 和 until
while循环的语法格式:
while 测试条件; do
循环体
done
while循环适用于循环次数未知,或不便用for直接生成较大的列表时;
而且如果测试结果为“真”,则进入循环;退出条件为,测试条件为假;
示例:求100以内整数的和

#!/bin/bash
#
declare -i count=1
declare -i sum=0
while [ $count -le 100 ]; do
let sum+=$count
let count++
done
echo $sum
显示当前系统上所有挂载的文件系统中空间使用百分比大于10的设备和空间使用百分比;使用while循环实现;
#!/bin/bash
#
df -P |( while read line;do
if echo $line | grep -o "\<[[:digit:]]\{1,\}%" > /dev/null;then
usedspace=`echo $line | grep -o "\<[[:digit:]]\{1,\}%" | cut -d% -f1`
device=`echo $line | cut -d' ' -f1`
if [ $usedspace -gt 10 ];then
echo "$device space has used ${usedspace}%."
fi
else
echo $line &> /dev/null
fi
done )
until循环的语法格式:

until 测试条件; do
循环体
done
如果测试结果为“假”,则进入循环;退出条件为,测试条件为真;
示例:求100以内所有正整数之和
#!/bin/bash
#
declare -i count=1
declare -i sum=0
until [ $count -gt 100 ]; do
let sum+=$count
let count++
done
echo $sum
示例:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;
显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止;
#!/bin/bash
#
read -p "Plz enter a username: " userName
while [ "$userName" != 'q' -a "$userName" != 'quit' ]; do
if id $userName &> /dev/null; then
grep "^$userName\>" /etc/passwd | cut -d: -f3,7
else
echo "No such user."
fi
read -p "Plz enter a username again: " userName
done
上面题目使用until循环的写法如下:
#!/bin/bash
#
read -p "Please input a username: " user
until [ "$user" == 'q' -o "$user" == 'quit' ];do
if id $user &> /dev/null;then
userid=`grep "^$user" /etc/passwd | cut -d: -f3`
usershell=`grep "^$user" /etc/passwd | cut -d: -f7`
echo "$user id is $userid.shell is $usershell."
else
echo "No such user."
fi
read -p "Please input a username again: " user
done
bash中for循环的另外一种格式
for ((初始条件;测试条件;修改表达式)); do
循环体
done
示例:求100以内所有偶数之和;
declare -i evensum=0
for ((counter=2; $counter <= 100; counter+=2)); do
let evensum+=$counter
done

bash编程之case语句
当我们需要做判断的条件情况很多时,如果使用多个if...else语句会显得句子结构不清晰,这个时候,我们可以选择case语句。
case语句的语法格式:
case 变量引用 in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
...
*)
分支n
;;
esac
语法格式中的PATTERN类同于文件名通配机制,但支持使用|表示或者;
a|b: a或者b
*:匹配任意长度的任意字符
?: 匹配任意单个字符
[]: 指定范围内的任意单个字符
示例:用户键入字符后判断其所属的类别;
#!/bin/bash
#
read -p "Plz enter a char: " char
case $char in
[0-9])
echo "a digit"
;;
[a-z])
echo "a char"
;;
*)
echo "a special word"
;;
esac
示例:
写一个脚本,使用格式:
script.sh {start|stop|restart|status}
1) start: 创建/var/lock/subsys/script.sh
2) stop: 删除此文件
3) restart: 先删除文件,再创建文件
4) status: 如文件存在,显示running,否则,显示stopped
#!/bin/bash
#
srv=`basename $0`
lockFile="/var/lock/subsys/$srv"
[ $# -lt 1 ] && echo "Usage: $srv {start|stop|restart|status}" && exit 4
[ $UID -ne 0 ] && echo "Only root." && exit 5
case $1 in
start)
if [ -f $lockFile ]; then
echo "$srv is running."
exit 7
else
touch $lockFile
[ $? -eq 0 ] && echo "Starting $srv OK."
fi
;;
stop)
if [ -f $lockFile ]; then
rm -f $lockFile
[ $? -eq 0 ] && echo "Stopping $srv OK."
else
echo "$srv is stopped yes."
exit 6
fi
;;
restart)
if [ -f $lockFile ];then
rm -f $lockFile
[ $? -eq 0 ] && echo "Stopping $srv OK."
else
echo "$srv is stopped yet."
fi
touch $lockFile
[ $? -eq 0 ] && echo "Starting $srv OK."
;;
status)
if [ -f $lockFile ];then
echo "$srv is running."
else
echo "$srv is stopped."
fi
;;
*)
echo "Usage: $srv {start|stop|restart|status}" && exit 9
esac
写一个脚本,对/etc/目录及内部的所有文件打包压缩
1、显示一个菜单,让用选择使用的压缩工具:
xz) xz compress tool
gz) gzip compress tool
bz2) bzip2 compress tool
2、根据用户选择的工具,对/etc执行相应的操作并保存至/backups目录,文件形如/backups/etc-日期时间.tar.压缩后缀
#!/bin/bash
#
[ -d /backups ] || mkdir /backups
cat << EOF
xz) xz compress tool
gz) gzip compress tool
bz2) bzip2 compress tool
EOF
read -p "Plz choose an option: " choice
case $choice in
xz)
tar -Jcf /backups/etc-`date +%F-%H-%M-%S`.tar.xz /etc/*
;;
gz)
tar -zcf /backups/etc-`date +%F-%H-%M-%S`.tar.gz /etc/*
;;
bz2)
tar -jcf /backups/etc-`date +%F-%H-%M-%S`.tar.bz2 /etc/*
;;
*)
echo "wrong option."
exit 1
;;
esac
写一个脚本,使用形式如下所示
showifinfo.sh [-i INTERFACE|-a] [-v]
要求:
1、-i或-a不可同时使用,-i用于指定特定网卡接口,-a用于指定所有接口;
显示接口的ip地址
2、使用-v,则表示显示详细信息
显示接口的ip地址、子网掩码、广播地址;
3、默认表示仅使用-a选项;
#!/bin/bash
#
verbose=0
allInterface=0
ifflag=0
interface=0
while [ $# -ge 1 ]; do
case $1 in
-a)
allInterface=1
shift 1
;;
-i)
ifflag=1
interface="$2"
shift 2
;;
-v)
verbose=1
shift
;;
*)
echo "wrong option"
exit 2
;;
esac
done
if [ $allInterface -eq 1 ]; then
if [ $verbose -eq 1 ]; then
ifconfig | grep "inet addr:"
else
ifconfig | grep "inet addr:" | awk '{print $2}'
fi
fi
if [ $ifflag -eq 1 ]; then
if [ $verbose -eq 1 ]; then
ifconfig $interface | grep "inet addr:"
else
ifconfig $interface | grep "inet addr:" | awk '{print $2}'
fi
fi

bash编程函数:

函数可以实现脚本的模块化,可以实现代码重用,可以使脚本结构更加清晰
函数的语法格式:
function F_NAME {
函数体
}
F_NAME() {
函数体
}
函数可以被调用,调用的方法就是使用函数名,凡是函数名出现的地方,会被自动替换为函数定义的那段代码;
函数的执行会有返回值,一般情况下函数的返回值有以下几种:
函数的执行结果返回值:代码的输出
函数中的打印语句:echo, print
函数中调用的系统命令执行后返回的结果
执行状态返回值:
函数体中最后一次执行的命令状态结果
自定函数执行状态的返回值:return #
函数可以接受参数:在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:如位置参数 $1, $2, ... $#,$*,$@

示例:服务脚本示例
#!/bin/bash
#
# chkconfig: 2345 67 34
#
srvName=$(basename $0)
lockFile=/var/lock/subsys/$srvName
start() {
if [ -f $lockFile ];then
echo "$srvName is already running."
return 1
else
touch $lockFile
[ $? -eq 0 ] && echo "Starting $srvName OK."
return 0
fi
}
stop() {
if [ -f $lockFile ];then
rm -f $lockFile &> /dev/null
[ $? -eq 0 ] && echo "Stop $srvName OK" && return 0
else
echo "$srvName is not started."
return 1
fi
}
status() {
if [ -f $lockFile ]; then
echo "$srvName is running."
else
echo "$srvName is stopped."
fi
return 0
}
usage() {
echo "Usage: $srvName {start|stop|restart|status}"
return 0
}
case $1 in
start)
start
;;
stop)
stop ;;
restart)
stop
start ;;
status)
status ;;
*)
usage
exit 1 ;;
esac
示例:写一个脚本,完成如下功能(使用函数):
1、提示用户输入一个可执行命令;
2、获取这个命令所依赖的所有库文件(使用ldd命令);
3、复制命令至/mnt/sysroot/对应的目录中
解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复制到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中;
4、复制各库文件至/mnt/sysroot/对应的目录中;

#!/bin/bash
#
target=/mnt/sysroot/
[ -d $target ] || mkdir $target
preCommand() {
if which $1 &> /dev/null; then
commandPath=`which --skip-alias $1`
return 0
else
echo "No such command."
return 1
fi
}
commandCopy() {
commandDir=`dirname $1`
[ -d ${target}${commandDir} ] || mkdir -p ${target}${commandDir}
[ -f ${target}${commandPath} ] || cp $1 ${target}${commandDir}
}
libCopy() {
for lib in `ldd $1 | egrep -o "/[^[:space:]]+"`; do
libDir=`dirname $lib`
[ -d ${target}${libDir} ] || mkdir -p ${target}${libDir}
[ -f ${target}${lib} ] || cp $lib ${target}${libDir}
done
}
read -p "Plz enter a command: " command
until [ "$command" == 'quit' ]; do
if preCommand $command &> /dev/null; then
commandCopy $commandPath
libCopy $commandPath
fi
read -p "Plz enter a command: " command
done

bash编程之信号捕捉和循环控制
在有些情况下,我们需要中止脚本的运行,需要在脚本运行过程中退出,于是,我们就需要在脚本中为脚本提供一些额外的信号,然后脚本在收到信号以后,做额外的处理动作,比如:
trap 'exit 1' SIGINT;这条语句的效果就是当脚本运行过程中如果我们输入SIGINT信号即Ctrl + c以后,脚本就会执行exit 1操作而退出脚本。
信号捕捉的语法格式:trap 'COMMAND;COMMAND' SIGNAL;kill和term信号是无法捕捉的。
bash循环控制命令:
continue: continue的作用是提前进入下一轮循环,当一个循环体中遇到continue语句,本次循环就不再执行,无论循环体中是否还有其他语句,而提前进入下一轮循环,它使用于条件语句中,仅在某些个特殊场景提前进入;
示例:求100以内3的整数倍的和
#!/bin/bash
#
declare -i sum=0
for i in {1..100};do
if [ $[$i%3] -ne 0 ];then
continue
fi
let sum+=$i
echo $sum
break
:在循环体中遇到break语句,就会跳出当前循环,效果是中止当前的循环体,它也是用于条件语句中,如果命令后面跟上数字,可以指定跳出几层循环
示例:提示用户输入一个用户,显示用户是否登录
#!/bin/bash
#
read -p "Plz enter a username: " userName
while true; do
if who | grep "\<$userName\>" &> /dev/null; then
break
fi
echo "not here."
sleep 5
done
echo "$userName is logged on."

bash编程之数组
当我们需要保存同一类型的变量,且变量的数量很多的情况下,就需要用到数组,数组也是变量,是内存中的存储单元,不过它是连续的多个存储单元,这些存储空间中存储的数据类型是一致的。之前我们引用每个变量时是通过变量名称来引用的,如果把多个变量定义为数组,那么在引用数组内的变量时,可以使用数组名+索引的方式来引用
数组中索引的表示方式:
数字索引:在bash中数组的数字索引从0开始定义:a[index]
如:a[0], a[1]
关联数组:数组中的元素使用自定义的名称的方式,在引用的时候使用数组名+元素自定义名称
如:a[hello],a[hi]
数组的定义方式:
declare -a ARRAYNAME;定义一个名称为ARRAYNAME的数组
示例:declare -a a
a[0]=$RANDOM
a[1]=$RANDOM
declare -A ARRAYNAME;定义一个名称为ARRAYNAME的关联数组
示例:declare -A c
c[hello]=$RANDOM;
c[hi]=$RANDOM;
数组的赋值方式:
一次只对一个元素赋值:比如,a[0]=$RANDOM,a[1]=$RANDOM,a[2]=$RANDOM...
一次对所有元素赋值:比如,a=(red green yellow blue)
使用索引的方式对部分元素进行赋值,bash中支持稀疏格式,可以不定义全部元素的值:比如,a=([0]=green [3]=red [2]=blue [6]=yellow),其中的a[1],a[4],a[5]元素为空
使用命令引用的方式来赋值:比如,logs=($(ls /var/log/*.log)),效果是将命令ls /var/log/*.log执行结果赋值给数组logs
使用用户输入的方式来给数组赋值,使用read命令:比如,# read -a A;如果用户输入的是a b c d ;那么在引用时${A[0]}=a ${A[1]}=b ${A[2]}=c ${A[3]}=d
数组中变量的引用方式:
索引访问:{a[1]};数组中的元素引用时必须用{}将数组名誉元素索引或者是关联索引扩起来,否则会被bash理解为变量a加上一个[1]字串,所以{}不可省。
比如:# echo ${a[1]};显示数组a的第二个元素的值
注:bash中的数组仅支持一维数组
数组的元素个数即数组的长度计算,有两种方式:
${#ARRAY[*]}
${#ARRAY[@]}
上面两种表达式是计算整个数组的元素个数
${#ARRAY[1]};这个表达式是计算数组中第二个元素的字符的个数
${#ARRAY};这个表达式是计算数组中第一个元素中的字符个数,这个表达式和 $ARRAY的效果相同

示例:写一个脚本,生成10个随机数,保存至数组中,而后显示数组下标为偶数的元素;
#!/bin/bash
#
for i in {0..9};do
num[$i]=$RANDOM
if [ $[$i%2] -eq 0 ];then
echo ${num[$i]}
fi
done
从数组中挑选某元素:
数组支持切片功能,我们可以使用如下方法取得需要的数组元素
${ARRAY[@]:offset:length};其中ARRAY为数组的名称@表示数组中的全部元素,offset为从数组起始元素开始的偏移量,length为需要取的元素的个数
示例:# A=(a b c d )
# echo ${A[@]:1:2}
b c ;取得的结果为b c
# echo ${A[@]:2}
c d;冒号后面只有一个数值时,表示偏移量为2,然后取出后面所有数组的值
# echo ${A[@]};表示取出所有元素
# echo ${A[@]:0:2};表示取出数组中前面的2个元素
数组的复制:某些情况下我们需要将某数组的值复制给其他数组,格式如下:array=(${num[@]});这样我们就将数组num的所有元素复制给了新数组array
示例:复制一个数组中下标为偶数的元素至一个新数组中
#!/bin/bash
declare -a mylogs
logs=(/var/log/*.log)
echo ${logs[@]}
for i in `seq 0 ${#logs[@]}`; do
if [ $[$i%2] -eq 0 ];then
index=${#mylogs[@]}
mylogs[$index]=${logs[$i]}
fi
done
echo ${mylogs[@]}
从数组中删除元素:unset ARRAY[index]
示例:数组logs=(/var/log/*.log),删除其中的第0个元素
# unset logs[0]

bash编程之字串处理:bash中的字串处理与取数组中连续的某些元素功能相似,是取出字符串中连续的部分子串,其语法如下:
${string:offset:long}
示例:
# string='hello world'
# echo ${string:2:4}
# echo ${string:2:5}
# echo ${string:2}
# echo ${string: -2};从右向左,取后面的2个字符
字串处理可以基于模式取子串
示例:
# echo ${variable#word};在variable中存储字串上,自左而右,查找第一次出现word,删除字符开始至此word处的所有内容;
# echo ${variable##word};在variable中存储字串上,自左而右,查找最后一次出现word,删除字符开始至此word处的所有内容;
# file=/var/log/messages
# echo ${file#/*e};结果是输出ssages
# echo #{file##*e};结果输出是一个字母s
# echo ${file#*/};返回结果是var/log/messages
# echo ${file##*/};返回messages,效果同取基名
# echo ${variable%word*}:在variable中存储的字串上,自右而左,查找第一次出现word,删除此word处至字串尾部的所有内容;
# echo ${variable%%word*}:在variable中存储的字串上,自右而左,查找最后最后一次出现的word,删除此word处至字串尾部的所有内容;
file='/var/log/messages'
# echo ${file%/*};返回的结果是/var/log
# echo ${file%%/*};返回的结果是空
phonenumber='010-110'
# echo ${phonenumber%%-*};取区号
# echo ${phonenumber%-*};取电话号码
phonenumber='010-110-8'
# echo ${phonenumber##*-};取接线员的号码,只显示8
url="http://www.magedu.com"
# echo ${url##*/};取主机名
url="http://www.magedu.com:80"
# echo ${url%:*};取端口号
字串的查找替换
# tail -1 /etc/passwd | sed "s@fedora@centos@g";将最后一行中的fedora替换成centos
# userinfo=`tail -1 /etc/passwd`
# echo $userinfo
# echo ${userinfo/fedora/centos};将/etc/passwd文件中最后一行中的第一个fedora字符串替换成centos
# echo ${userinfo//fedora/centos};将/etc/passwd文件中最后一行里面的所有fedora字符串替换成centos
查找替换的语法格式:
${variable/pattern/substi};替换一个字串
${variable//pattern/substi};替换所有的出现
# echo ${userinfo/#fedora/centos};位置锚定的效果,锚定行首的fedora,再使用centos替换
# useradd -d /home/test hello
# userin=`tail -1 /etc/passwd`
# echo $userin
# echo ${userin/#test/TEST};这个语句的意思是替换行首的test换成大写的TEST,但是之前添加的用户是hello,家目录是test,这个test在行的中间位置,所以命令执行的时候是不会替换的
# echo ${userin/test/TEST};这个操作会替换行中出现的test成TEST
# useradd bash
# user=`tail -1 /etc/passwd`
# echo ${user/#bash/BASH};替换行首的bash为BASH
# echo ${user/bash/BASH};替换行中第一个字串bash为BASH
# echo ${user//bash/BASH};替换行中多个字串bash为BASH
# echo ${user/%bash/BASH};替换行尾的bash为BASH
字符串的查找删除操作:
# echo ${variable/pattern}
# echo ${user/bash};删除user变量中第一个bash字符串
# echo ${user//bash};删除user变量中全部bash字符串
# echo ${user/#bash};删除user变量中行首的bash
# echo ${user/%bash};删除user变量中行尾的bash
字符串的大小写转换:
小-->大:${variable^^}
大-->小:${variable,,}
# echo ${user^^}
# echo ${user,,}
注:替换被模式匹配到的字串替换大小写,这种操作只能改变变量中的单个字符,将单个字符大小写改变
示例:
# echo ${user^^s};可以将user变量中的字母s改变成大写,不支持更改字符串的大小写。
变量赋值操作:
${parameter:-word}:如果parameter这个变量没有被设定或赋值,那么整个表达式的值会被设置为word,而parameter的值不会发生变化,还是为空,如果有值,那么就显示原有的值
# url='www.magedu.com'
# echo ${url:-mageedu.com};返回的结果是www.magedu.com
# unset url
# echo ${url:-mageedu.com};返回的结果是mageedu.com
# echo $url;这时候显示的结果为空
${parameter:=word}:如果parameter这个变量没有被设定或赋值,那么整个表达式的值会被设置为word,而且parameter的值会被赋值为word,如果parameter有值,那么表达式的值是parameter的值,这个赋值表达式有赋默认值的效果
# url='www.magedu.com'
# echo ${url:=mageedu.com};返回的值是www.magedu.com
# unset url
# echo ${url:=mageedu.com};此时返回的值是mageedu.com
# echo $url;此时url这个变量被赋值为mageedu.com,变量赋值的效果
${parameter:+word}:如果parameter变量没有被设定或赋值,那么整个表达式就不被赋值,显示的时候会是空,如果parameter有初始值,那么整个表达式的值就使用后面的word来显示,这个应用的场景是某个变量的值作为标志位,如果标志位为什么值,那么就启用什么特性。如果这个标志位没有,那么就把它的特性关闭,比如在脚本中使用DEBUG功能,如果这个DEBUG变量的值设为1,那么后面的选项就有效,如果DEBUG变量的值没有设置或者为空,那么后面的选项就无效了。
${parameter:?word}:如果parameter变了没有被设定或赋值,那么整个表达式在被调用时会显示word字串的信息,可以作为命令的错误提示信息使用
示例:
# debug=1
# echo ${debug:+verbose};当debug的值为1时,整个表达式的值就是verbose
# unset debug
# echo ${debug:+verbose};没有设置debug的值,整个表达式的值就为空
# echo ${debug:?null};当debug没有值时,会显示-bash:debug:null
# echo $?;返回的值也是非0值

为脚本使用配置文件,并确保其变量有可用值的方式
variable=${variable:-default value}
脚本示例:读取/etc/sysconfig/network文件,利用其HOSTNAME变量的值设置主机名;
#!/bin/bash
#
[ -f /etc/sysconfig/network ] && source /etc/sysconfig/network
host=${HOSTNAME:-www.magedu.com}
hostname $host
hostname
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息