BASH 编程之变量高级篇

内部变量

• $$与$BASHPID都代表着执行程序的进程 ID,我们可以通过 echo 打印,并用 ps 指令检查得到相同的进程 ID
[root@oracle ~]# echo $BASHPID #没有结果??
[root@oracle ~]# echo $$ #显示执行进程的 id3131
[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
usage : $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
原文地址:https://www.cnblogs.com/zoujiaojiao/p/10938328.html