内部变量
• $$与$BASHPID都代表着执行程序的进程 ID,我们可以通过 echo 打印,并用 ps 指令检查得到相同的进程 ID
[root@oracle ~]# echo $BASHPID #没有结果??[root@oracle ~]# echo $$ #显示执行进程的 id 号3131[root@oracle ~]# ps ax | grep bash2589 ? Ss 0:00 /usr/bin/ssh-agent /bin/sh -c exec -l /bin/bash -c"/usr/bin/dbus-launch --exit-with-session /etc/X11/xinit/Xclients"2780 pts/1 Ss+ 0:00 bash3041 ? S 0:00 /bin/bash /usr/bin/eio3131 pts/2 Ss 0:00 bash3161 pts/2 R+ 0:00 grep bash
位置参数
我们经常向一个程序传递以空格间隔的参数,我们按先后次序分成$0(脚本本身) ,$1(参数 1),$2(参数 2).....
实例:如果没有输入参数则提示错误并显示用法,返回-1 值;输入了就说明对了,返回 0
#!/bin/bash[ ! -n "$1" ] && echo -e "wrong\nusage : $0 canshu1 " && exit -1 || echo "right"#-n 测试参数的长度是否为 0,不为 0 则返回 0,表示 true测试:[root@oracle ~]# ./f2.sh 没有接参数wrongusage : ./f2.sh canshu1[root@oracle ~]# echo $?255[root@oracle ~]# ./f2.sh ff 接了参数right[root@oracle ~]# echo $?0
当这些参数在“”之间时:
• $#表示参数数量的总数,也就是总共有多少参数
• $*所有的参数以一行显示
• $@所有的参数以空格分隔
• let 可以对 C语言的表达式做计算,还有另外一种方法实现$((index+=1))
实例:区别以上符号代表的意义
1、$##!/bin/bashin=1for arg in "$#"doecho "arg $in = $arg"let "in=in+1"done测试结果:$#表示参数总数[root@oracle ~]# ./f1.sh a b carg 1 = 32、$*#!/bin/bashin=1for arg in "$*"doecho "arg $in = $arg"let "in=in+1"done测试:$*参数以一行显示[root@oracle ~]# ./f1.sh a b carg 1 = a b c3、$@#!/bin/bashin=1for arg in "$@"doecho "arg $in = $arg"$((in+=1))done测试:$@参数以空格为分隔[root@oracle ~]# ./f1.sh a b carg 1 = aarg 2 = barg 3 = c
强制退出程序本身
•$!代表进程本身
•eval可以对字符串的代码当成 bash 代码并运行,等同于` `和$( )
•(())中变量无需在变量前使用$,而且没有空格的局限性
[root@oracle ~]# vim &\> { sleep 2;> eval 'kill -9 $!' &> /dev/null;> }[1] 3855#停了 2 秒[1]+ Stopped vimYou have new mail in /var/spool/mail/root[root@oracle ~]#[1]+ 已杀死 vim
字符长度
这三种方法都可以取出字符串的长度
${#string}
expr length $string
expr " $string" : ' . * '
实例:如果输入的 id 号小于 6 个字符就告诉用户是非法的 id 号,大于 6 是合法的 id 号
#!/bin/bashecho -e "please input you id:"read string#ln=${#string} #这 2 句注释掉的和下面的那句起同样的作用#ln=$( expr "$string" : '.*')ln=$( expr length $string )((ln < 6)) && echo "$string shi fei fa de id hao" || echo " id $string shi he fa de "
表达式方式取长度
•依据正则表达式取得相匹配部分的长度,注意表达式要用单引号
• expr match "$string"' $substring '
• expr " $string" : ' $substring'
[root@desktop Desktop]# num=1234HHHjjj123[root@desktop Desktop]# echo $num1234HHHjjj123[root@desktop Desktop]# echo ${num}1234HHHjjj123[root@desktop Desktop]# echo ${#num}
13
[root@desktop Desktop]# echo "${num}kk"
1234HHHjjj123kk
[root@oracle ~]# stringZ=123aaa123bbb456ccc
接下来的 2 句脚本查找的是 123aaa123bbb4 的长度:13
[root@oracle ~]# echo `expr match "$stringZ" '123[a-z1-9]*.4'`
13
[root@oracle ~]# echo `expr "$stringZ" : '123[a-z1-9]*.4'`
13
123[a-z1-9]*.4:必须从字符串的开始字符匹配,[a-z1-9]表示字母 a-z和数字1-9,
*.4 表示这些字符出现任意次后以 4 结尾
符号提取
• 根据字符的位置提取字符串中一段,注意此方法索引由 0 开始
• ${string:position} 是从 0 开始的哦
• ${string:position : length} 冒号之间没有空格哦
实例:
[root@oracle ~]# stringZ=123aaa123bbb456ccc不要前面 6 个字符:[root@oracle ~]# echo ${stringZ:6}123bbb456ccc从编号为 3 的字符,即第 4 个字符开始,提取 6 个字符[root@oracle ~]# echo ${stringZ:3:6}aaa123
函数提取
•也可以使用 substr函数提取,注意这个函数索引从 1 开始
•expr substr $string $position $length
[root@oracle ~]# stringZ=123aaa123bbb456ccc从第 6 个字符即 a 开始提取 6 个字符:[root@oracle ~]# echo `expr substr $stringZ 6 6`a123bb
实例:取一个字符串中的日期
[root@desktop Desktop]# SN=WINDD20110908DDSL[root@desktop Desktop]# echo ${SN:5:4}-${SN:9:2}-${SN:11:2}2011-09-08
前面提取字符串
• 按照正则表达式从前面开始提取字符串,注意表达式写在\ ( \ )
• expr match "$string"'\ ( $substring \ ) '
• expr " $string" : ' \($substring\ ) '
实例: stringZ=123aaa123bbb456ccc
[root@oracle ~]# echo `expr match "$stringZ" '\(123[a-z]..[1-9]*\)'`123aaa123[root@oracle ~]# echo `expr "$stringZ" : '\(123[a-z]..[1-9]*\)'`123aaa123
后面提取字符串
•按照正则表达式从后面开始提取字符串,注意表达式写在\ ( \ )
•.*中“.”表示任何字符,“*”表示 0 到无穷的匹配
•expr match "$string" '.* \($substring\) '
•expr " $string" : '.*\($substring\)
实例: stringZ=123aaa123bbb456ccc
[root@oracle ~]# echo `expr "$stringZ" : '.*\([a-z]...\)'`b456
前面字符串移除
• 按照正则表达式从前面开始移除字符串中的部分
• ${string #substring}仅移除最先匹配的部分
• ${string ##substring}只要是匹配统统移除
实例:[root@oracle ~]# stringQ=qqww20081010aabb
[root@oracle ~]# echo "${stringQ#q*w}" 最短匹配w20081010aabb[root@oracle ~]# echo "${stringQ##q*w}" 最长匹配20081010aabb
实例:找出内核版本号
[root@desktop Desktop]# cat /boot/grub/grub.conf |grep kernel|grep 2.6 |cut-d ' ' -f 2/vmlinuz-2.6.32-71.el6.x86_64[root@desktop Desktop]# VV=$(cat /boot/grub/grub.conf |grep kernel|grep 2.6|cut -d ' ' -f 2)[root@desktop Desktop]# echo ${VV#/[a-z]*-}2.6.32-71.el6.x86_64
后面字符串移除
• 按照正则表达式从后面开始移除字符串中的部分
•${string%substring}仅移除从后面开始最先匹配的部分
•${string%%substring}只要是匹配统统移除
实例:[root@oracle ~]# stringQ=qqww20081010aabb
[root@oracle ~]# echo "${stringQ%0*b}" 从后面最短匹配qqww2008101[root@oracle ~]# echo "${stringQ%%0*b}" 从后面最长匹配qqww2
练习:重命名所有在/roo/Desktop 下非 txt 后缀的文件,将其变成"txt"后缀比如"file1.TXT"将变成" file.txt" ...,这个可以很好的解决 windows 文件拷入到 Linux 中的问题
#!/bin/bashDIR=$1fix="TXT txT tXt tXT Txt TXt TxT"for LINE in $fixdoold=$LINEnew=txtfor FILE in $(find $DIR -name "*.$old")domv -v $FILE ${FILE%$old}$newdonedone测试:[root@desktop Desktop]# ./chname.sh ./`./2.TXT' -> `./2.txt'`./3.TXT' -> `./3.txt'`./1.TXT' -> `./1.txt'
表达式方式字符串替换
• 有点雷同与 sed 的表达式
• ${string / substring /replacement}首先匹配的被替换
• ${string/ /substring/replacement}匹配的全部替换
•从前面很多的例子中我们可以看到在${}中“#”代表从前面,“ %”代表从后面,因此
${string/#substring / replacement}从前面查找并替换;
${string /%substring / replacement}从后面查找并替换
参数替换
•${parameter}与直接使用$parameter 相同,但在很多的情况下使用${}可以减少歧义。
•${parameter-default} , ${parameter: -default}
•如果参数没有设置,将使用默认值
N 声明了,只是没有设置参数[root@desktop Desktop]# N=[root@desktop Desktop]# N1=qq[root@desktop Desktop]# echo "${N-dd}" 没有设置参数的 N 没被替换You have new mail in /var/spool/mail/root[root@desktop Desktop]# echo "${N:-dd}" 没有设置参数的 N 被替换dd[root@desktop Desktop]# echo "${N1-dd}" 设置了参数的 N1 没被替换qq[root@desktop Desktop]# echo "${N1:-dd}" 设置了参数的 N1 没被替换qq没有声明的 N2,被默认值替换[root@desktop Desktop]# echo "${N2-dd}"dd[root@desktop Desktop]# echo "${N2:-dd}"dd
DEFAULT_FILENAME=generic.data
filename = ${1:-$DEFAULT_FILENAME}
以上两句等同于下面的一段代码
# 如果位置第一个位置参数长度为 0( 没有设置$1)
if [ ! -n $1 ]
# 那么
then
#filename 设置为缺省的值
filename=gerneric.data fi
•下面的表达式与之前的表达式雷同,细微的不同的${parameter -default}只判断参数是 否已经设置${parameter = default} ,${parameter:=default} 如果参数已经设置,但为空,将使用缺省值 echo ${username=`whoami`} 此处,之前没有定义过 username,那么变量将设置为`whoami`命令的结果
if [ -n $username ]; then
username=$username
else
username=`whoami`
fi
[root@desktop Desktop]# M=[root@desktop Desktop]# M1=rr[root@desktop Desktop]# echo "${M=aa}"[root@desktop Desktop]# echo "${M:=aa}"aa[root@desktop Desktop]# echo "${M1=aa}"rr[root@desktop Desktop]# echo "${M1:=aa}"rr[root@desktop Desktop]# echo "${M2=aa}"aa[root@desktop Desktop]# echo "${M2:=aa}"aa
•如果参数设置了,使用替换值,否则清空变量${parameter+alt} ,${parameter:+alt} 逻辑等同于:
if [ -n $parameter ]
then
parameter=$alt
else
# 清空此参数
parameter=
fi
例子:
a=${param1+xyz} # 由于 param1 没有设置所以 a 为空
echo "a = $a" # a =
param2= # param2 设置为空
a=${param2+xyz}
echo "a = $a" # 有设置将替换成缺省值 a = xyz
param3=123 # param3 设置为 123
a=${param3+xyz}
echo "a = $a" # 有设置将替换成缺省值 a = xyz
[root@desktop Desktop]# H=[root@desktop Desktop]# H1=oo[root@desktop Desktop]# echo "${H+yy}"yy[root@desktop Desktop]# echo "${H:+yy}"[root@desktop Desktop]# echo "${H1+yy}"yy[root@desktop Desktop]# echo "${H1:+yy}"yy[root@desktop Desktop]# echo "${H2+yy}"[root@desktop Desktop]# echo "${H2:+yy}"
错误检测
•判断参数是否设置,没有将打印后面的错误信息,${parameter ? error_msg}, $
{parameter :? error_msg}
•下面的例子可以直接对系统变量检查,如果没有设置,将直接退出程序
${HOSTNAME?} ${USER?} ${HOME?} ${MAILBOX ?}
:${ZZXy23AB ? "ZZXy23ABhasnotbeenset"}
${1 ? "Usage:$0 x.x.x.x"}# 最为简单的方式检查是否设置位置 1 参数
综合练习
实例 1:echo `basename $PWD` # 打印当前工作路径最后一段子目录名字echo "${PWD##*/}" # 另一种方法的实现echo `basename $0` # 得到脚本的名字echo $0 # 另一种方法的实现echo "${0##*/}" # 另一种方法的实现filename=test.dataecho "${filename##*.}" # 移除"."之前的内容,得到 data 文件名的后缀实例 2:下面的例子实现将特定的文件名后缀,比如 .gif 统统改为其他后缀for filename in *.$1 # 遍历当前整个目录中所有指定的文件后缀domv $filename ${filename%$1}$2#去除掉输入的文件名后缀再追加另外指定的一个doneexit 0实例 3:path_name=/home/bozo/ideas/thoughts.for.todayecho "path_name = $path_name"t=${path_name##/*/} # 根据表达式最大可能的移除/部分,最后只留下文件名echo "path_name, stripped of prefixes = $t"t=${path_name%/} ; t=${t##*/}; # 同样的效果实例 4:下面的例子将一个用户 yangwawa 主目录的文件拷贝到另一个用户 joe(可以用$1代替写在脚本中)的主目录中FILENAME=/home/yangwawa/.bash_profilecp -v $FILENAME{,${FILENAME/yangwawa/joe}}
提取目录中的文件名:[root@desktop Desktop]# FILENAME=/etc/sysconfig/network-scripts/ifcfg-eth0[root@desktop Desktop]# basename $FILENAMEifcfg-eth0[root@desktop Desktop]# echo ${FILENAME##*/}ifcfg-eth0[root@desktop Desktop]# echo ${FILENAME%/*}/etc/sysconfig/network-scripts拼接目录:[root@desktop Desktop]# BACK_DIR=/usr/local/share[root@desktop Desktop]# echo $BACK_DIR/${FILENAME##*/}/usr/local/share/ifcfg-eth0替换文件路径中目录的名字:[root@desktop Desktop]# FILENAME=/home/student/.mozilla/firefox/profiles.ini[root@desktop Desktop]# echo ${FILENAME/student/visitor}/home/visitor/.mozilla/firefox/profiles.ini
定义一个变量
declare -r 定义一个只读变量
declare -i 定义的变量是一个数字,对于数字变量可以不用在变量名前使用"$"符号
declare -a 定义的是数组
declare -f 定义的是函数
declare -x 定义的变量在 bash 中等同于 export 可以为其他程序所用
declare 显示变量
• declare 命令对于标识变量也非常有用
bash$ declare | grep HOME
/home/bozo
bash$ zzy=68
bash$ declare | grep zzy
zzy=68
bash$ Colors=([0]="purple" [1]="reddish-orange" [2]="light green")
bash$ echo ${Colors[@]}
purple reddish-orange light green
bash$ declare | grep Colors
Colors=([0]="purple" [1]="reddish-orange"[2]="light green")
数组概述
• 申明 数组
declare - aarray
array[xx]
• 调用 时
${array[ xx] }
数组使用
打印书名:
#!/bin/bashdeclare -a BOOKSBOOKS[0]="Windows 2007"BOOKS[1]="Windows xp"BOOKS[2]="Oracle"BOOKS[3]="IBM ATX 5"BOOKS[4]="RedHat"echo "${BOOKS[@]}" #将数组作为一串字符打印出来echo ++++++++++++++++++++++TOTAL=${#BOOKS[@]} #以空格为依据取字符串的长度for ((X_B=0;X_B${BOOKS[$X_B]}"done测试:[root@desktop Desktop]# chmod u+x showbooks.sh[root@desktop Desktop]# ./showbooks.shWindows 2007 Windows xp Oracle IBM ATX 5 RedHat++++++++++++++++++++++0 --> Windows 20071 --> Windows xp2 --> Oracle3 --> IBM ATX 54 --> RedHat