内部变量
• $$与$BASHPID都代表着执行程序的进程 ID,我们可以通过 echo 打印,并用 ps 指令检查得到相同的进程 ID
[root@oracle ~]# echo $BASHPID #没有结果?? [root@oracle ~]# echo $$ #显示执行进程的 id 号 3131 [root@oracle ~]# ps ax | grep bash 2589 ? 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 bash 3041 ? S 0:00 /bin/bash /usr/bin/eio 3131 pts/2 Ss 0:00 bash 3161 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 没有接参数 wrong usage : ./f2.sh canshu1 [root@oracle ~]# echo $? 255 [root@oracle ~]# ./f2.sh ff 接了参数 right [root@oracle ~]# echo $? 0
当这些参数在“”之间时:
• $#表示参数数量的总数,也就是总共有多少参数
• $*所有的参数以一行显示
• $@所有的参数以空格分隔
• let 可以对 C语言的表达式做计算,还有另外一种方法实现$((index+=1))
实例:区别以上符号代表的意义
1、$# #!/bin/bash in=1 for arg in "$#" do echo "arg $in = $arg" let "in=in+1" done 测试结果:$#表示参数总数 [root@oracle ~]# ./f1.sh a b c arg 1 = 3 2、$* #!/bin/bash in=1 for arg in "$*" do echo "arg $in = $arg" let "in=in+1" done 测试:$*参数以一行显示 [root@oracle ~]# ./f1.sh a b c arg 1 = a b c 3、$@ #!/bin/bash in=1 for arg in "$@" do echo "arg $in = $arg" $((in+=1)) done 测试:$@参数以空格为分隔 [root@oracle ~]# ./f1.sh a b c arg 1 = a arg 2 = b arg 3 = c
强制退出程序本身
•$!代表进程本身
•eval可以对字符串的代码当成 bash 代码并运行,等同于` `和$( )
•(())中变量无需在变量前使用$,而且没有空格的局限性
[root@oracle ~]# vim &\ > { sleep 2; > eval 'kill -9 $!' &> /dev/null; > } [1] 3855 #停了 2 秒 [1]+ Stopped vim You have new mail in /var/spool/mail/root [root@oracle ~]# [1]+ 已杀死 vim
字符长度
这三种方法都可以取出字符串的长度
${#string}
expr length $string
expr " $string" : ' . * '
实例:如果输入的 id 号小于 6 个字符就告诉用户是非法的 id 号,大于 6 是合法的 id 号
#!/bin/bash echo -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 $num 1234HHHjjj123 [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/bash DIR=$1 fix="TXT txT tXt tXT Txt TXt TxT" for LINE in $fix do old=$LINE new=txt for FILE in $(find $DIR -name "*.$old") do mv -v $FILE ${FILE%$old}$new done done 测试: [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.data echo "${filename##*.}" # 移除"."之前的内容,得到 data 文件名的后缀 实例 2: 下面的例子实现将特定的文件名后缀,比如 .gif 统统改为其他后缀 for filename in *.$1 # 遍历当前整个目录中所有指定的文件后缀 do mv $filename ${filename%$1}$2 #去除掉输入的文件名后缀再追加另外指定的一个 done exit 0 实例 3: path_name=/home/bozo/ideas/thoughts.for.today echo "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_profile cp -v $FILENAME{,${FILENAME/yangwawa/joe}}
提取目录中的文件名: [root@desktop Desktop]# FILENAME=/etc/sysconfig/network-scripts/ifcfg-eth0 [root@desktop Desktop]# basename $FILENAME ifcfg-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/bash declare -a BOOKS BOOKS[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<TOTAL;X_B++)) do echo "$X_B --> ${BOOKS[$X_B]}" done 测试: [root@desktop Desktop]# chmod u+x showbooks.sh [root@desktop Desktop]# ./showbooks.sh Windows 2007 Windows xp Oracle IBM ATX 5 RedHat ++++++++++++++++++++++ 0 --> Windows 2007 1 --> Windows xp 2 --> Oracle 3 --> IBM ATX 5 4 --> RedHat