shell高级编程笔记(第三章 特殊字符)

第二部分 基本

第三章 特殊字符

1

“#” 注释
行首以#开头为注释(#!是个例外)。

注释也可以存在与本行命令的后边。

echo "A comment will follow"   #注释在这里

在echo命令中被转义的#是不能作为注释的。同样的,#也可以出现在特定的参数替换结构中或者是数字常量表达式中。
标准的引用和转义字符("’)可以用来转义#。

echo "The # here does not begin a comment."
echo 'The # here does not begin a comment.'
echo The \# here does not begin a comment.

echo ${PATH#*:}   #参数替换
echo $(( 2#101011 ))   #数制转换

2

“;” 命令分隔符,可以用来在一行中写多个命令。

echo hello;echo there

if [ -x "$filename" ];then   #注意:if和then需要分隔
   echo "File $filename exists.";cp $filename $filename.bak
else
  echo "File $filename not found";touch $filename
fi;echo "File test complete."     

3

“;;” 终止case选项。

case "$variable" in
abc)  echo "\$variable = abc";;
xyz)  echo "\$variable = xyz";;
esac

4

4.1

“.” .命令等价于source命令(见Example 11.20)。这是一个bash的内建命令

4.2

“.” .作为文件名的一部分。
如果作为文件名前缀的话,那么这个文件将成为隐藏文件。将不被ls命令列出。

[root@localhost shell]# ls -l
total 0
-rw-r--r--. 1 root root 0 Jun 30 20:05 test.sh

[root@localhost shell]# ls -al
total 8
drwxr-xr-x. 2 root root 4096 Jun 30 20:05 .
drwxr-xr-x. 4 root root 4096 Jun 30 20:04 ..
-rw-r--r--. 1 root root    0 Jun 30 20:05 .doc.txt
-rw-r--r--. 1 root root    0 Jun 30 20:05 test.sh

4.3

“.” .命令如果作为目录名的一部分的话,那么.表达的是当前目录。…表达上一级目录

[root@localhost shell]# pwd
/opt/shell

[root@localhost shell]# cd .
[root@localhost shell]# pwd
/opt/shell

[root@localhost shell]# cd ..
[root@localhost opt]# pwd
/opt

4.4

. .字符匹配,这是作为正则表达式的一部分,用来匹配任何单个字符。

5

" 部分引用。"字符串"阻止了一部分特殊字符,具体见第五章。

6

’ 全引用。'字符串’阻止了全部特殊字符,具体见第五章。

7

, 逗号连接了一系列的算术操作,虽然里边所有的内容都被运行了,但只有最后一项被返回。如:

let "t2=((a=9,15/3))"   #set "a=9" and "t2=15/3"
echo $t2   #5

8

\ 转义字符,如\X等价于"X"或’X’,具体见第五章。

9

/ 文件名路径分隔符或用来做出发操作。

10

` 后置引用,命令替换,具体见第十四章。

11

: 空命令,等价于"NOP"(no op,一个什么也不干的命令)。也可以被认为与shell的内建命令(true)作用相同。

11.1

“:” 命令是一个bash的内建命令,它的返回值为0,就是shell返回的true。
如:

echo $?   #返回值为0

在死循环中:

while :
do
  operation-1
  operation-2
  ...
  operation-n
done  

在if/then中的占位符:

if 条件
then :   #什么都不做,引出分支
else
  take-some-action
fi  

在一个2元命令中提供一个占位符,如:

: ${username=`whoami`}
# ${username=`whoami`}   如果没有":"的话,将给出一个错误,除非username是个命令

使用"参数替换"来评估字符串变量,如:

: {HOSTNAME?}${USER?}${MAIL?}   #如果一个或多个必要的环境变量没被设置的话,就打印错误信息

11.2

变量扩展/子串替换
在和>(重定向)结合使用的时候,把一个文件截断到0长度,没有修改它的权限;如果文件在之前不存在,那么就创建它。如:

: > data.txt   #文件data.txt现在被清空
#与cat /dev/null > data.txt的作用相同,但是不会产生一个新的进程,因为":"是一个内建命令

在和>>重定向操作符结合使用时,将不会对想要附加的文件产生任何影响;如果文件不存在,将创建。

注意:这只是用于正规文件,而不是管道、符号连接和某些特殊文件。也可能作为注释行,虽然不推荐这么做。使用#来注释的话,将关闭剩余行的错误检查,所以可以在注释行中写任何东西。然而使用:的话不会这样。如:

: This is a comment thar generates an error,(if [ $x -eq 3 ])

11.3

: 还用来在/etc/passwd和$PATH变量中用来做分隔符。

[root@localhost opt]# echo $PATH
/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/root/bin

12

! 取反操作符,将反转"退出状态"结果,(见Example 6.2)。也会反转test操作符的意义。比如修改=为!=。!=操作是Bash的一个关键字。

在一个不同的上下文中,!也会出现在"间接变量引用",见"Example 9.22"。

13

13.1

  • 万能匹配字符,用于文件名匹配(这个东西有个专有名词叫file globbing),或者是正则表达式中。
    注意:在正则表达式匹配中的作用和在文件名匹配中的作用是不同的。
[root@localhost shell]# ls -al
total 8
drwxr-xr-x. 2 root root 4096 Jun 30 20:05 .
drwxr-xr-x. 4 root root 4096 Jun 30 20:04 ..
-rw-r--r--. 1 root root    0 Jun 30 20:05 .doc.txt
-rw-r--r--. 1 root root    0 Jun 30 20:05 test.sh
[root@localhost shell]# echo *
test.sh

13.2

  • 数学乘法

14

? 测试操作。在一个确定的表达式中,用?来测试结果。

(())结构可以用来做数学计算或者写C代码,那?就是C语言的3元操作符的一个。

? 在"参数替换"中,?测试一个变量是否被set了

? 在文件扩展匹配(file globbing)中和正则表达式中匹配任意的单个字符

15

$ 变量替换

var1=5
var2=23skidoo

echo $var1
echo $var2

$ 在正则表达式中作为行结束符
${} 参数替换,见9.3节
∗ , *, ,@ 位置参数
? 退 出 状 态 。 ? 退出状态。 ?退?保存一个命令或一个函数或脚本本身的退出状态。
进 程 I D 变 量 。 这 个 进程ID变量。这个 ID变量保存运行脚本进程ID。

16

() 命令组

(a=hello;echo $a)

注意:在()中的命令列表将作为一个子shell来运行。
在()中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的。
如:

a=123
(a=321;)
echo "a = $a"   #a=123

在()内,a变量更像是一个局部变量

用在数组初始化,如:

Array=(element1,element2,element3)

17

{xxx,yyy,zzz…} 大括号扩展
如:

cat {file1,file2,file3} > combined_file
#把file1,file2,file3连接在一起,并重定向到combined_file中

cp file22.{txt,backup}
#拷贝file22.txt到file22.backup中

一个命令可能会对大括号中的以逗号分隔的文件列表起作用
注意:在大括号中,不允许有空白,除非这个空白是有意义的

echo {file1,file2}\:{\A,"B",'C'}
file1:A file1:B file1:C file2:A file:B file:C

18

{} 代码块
又被称为内部组。事实上,这个结构创建了一个匿名的函数;但是与函数不同的是,在其中声明的变量,对于脚本的其它部分代码来说还是可见的。
注意:与()中的命令不同的是,{}中的代码块将不能正常地开启一个新shell
如:

[root@localhost shell]# {
> local a;
> a=1;
> }
bash: local: can only be used in a function   #bash中local申请的变量只能够用在函数中。
a=123
{a=321;}
echo "a=$a"   #a=321.说明在代码块中对变量a所做的修改,影响了外边的变量a

下面的代码展示了在{}结构中代码的I/O重定向
Example 3.1 代码块和I/O重定向

#!/bin/bash
#从/etc/passwd中读行

File=/etc/passwd

{
read line1
read line2    
} < $File

echo "First line in $File is:"
echo "$line1"
echo
echo "Second line in $File is:"
echo "$line2"

exit 0

Example 3.2 将一个代码块的结果保存到文件(检测rpm包的信息)

#!/bin/bash
#rpm-check.sh

#这个脚本的目的是为了描述列表和确定是否可以安装一个rpm包
#在一个文件中保存输出

#这个脚本使用一个代码块来展示

SUCCESS=0
E_NOARGS=65

if [ -z "$1" ];then
  echo "Usage:`basename $0` rpm-file"
  exit $E_NOARGS
fi

{
echo
echo "Archive Description:"
rpm -qpi $1   #查询说明
echo
echo "Archive Listing:"
rpm -qpl $1   #查询列表
echo

rpm -i --test $1   #查询rpm包是否可以被安装
if [ "$?" -eq $SUCCESS ];then
  echo "$1 can be installed."
else
  echo "$1 cannot be installed."
fi
echo    
}  > "$1.test"   #把代码块中所有输出都重定向到文件中

echo "Results of rpm test in file $1.test"

exit 0

之前的IMEI监控脚本改进版(其中的"###"分割线不适用IMEI平台的报表)

#!/bin/bash
#获取当前时间(2020-06-22 16:01:39)
data=`date +'%Y-%m-%d %H:%M:%S'`
#文件名时间(20200622)
data2=`date +%Y%m%d`
#存放文件
LOG_FILE=/data/data/outtable/DIMEI_JK_9001$data2.txt

ROOT_UID=0
E_NOROOT=11

#判断是否是根用户
if [ $UID -ne $ROOT_UID ];then
    echo "Root user is required to run this script" >> $LOG_FILE
    exit $E_NOROOT
fi

#判断存放数据的文件是否存在
[ -f $LOG_FILE ] || touch $LOG_FILE


{
echo "########## start ##########"

#磁盘监控( 根分区 )
#disk_use=`df -h | grep "/dev/mapper/vg_zhhs-lv_root" | awk '{printf $5}' | cut -d '%' -f 1`
disk_use=`df -h | sed -n 3p | awk '{printf $4}' | cut -d '%' -f 1`
    echo "$data:root_partion:$disk_use%"
#磁盘监控( boot分区 )
disk_use2=`df -h | grep "/dev/sda1" | awk '{printf $5}' | cut -d '%' -f 1`
    echo "$data:boot_partion:$disk_use2%"

#cpu监控
cpu_average=`top -b -n 1 | grep "load" | awk '{printf $12}' | cut -d "." -f 1`   
    echo "$data:CPU:$cpu_average%"
	
#内存监控 
men_use=`free | awk '/Mem/{printf("%.2f\n"), $3/$2*100}' | cut -d "." -f 1`
    echo "$data:Memory:$men_use%"
	
echo "########## end ##########"
echo
}  >> $LOG_FILE

exit 0

19

{} ; 路径名。一般都在find命令中使用。这不是一个shell内建命令。
注意:;号用来结束find命令序列的-exec选项。

20

20.1

[] test。test的表达式在[]中。
值得注意的是[是shell内建test命令的一部分,并不是/usr/bin/test中的扩展命令的一个连接。

20.2

[] 数组元素

Array[1]=slot_1
echo${Array[1]}

20.3

[] 字符范围。在正则表达式中使用,作为字符匹配的一个范围。

20.4

[[]] test表达式放在[[]]中。(shell关键字)。具体查看[[]]结构的讨论。

21

(()) 数学计算的扩展。
在(())结构中可以使用一些数字计算。具体参阅((…))结构。

22

“>&>>&>><” 重定向

scriptname > filename   #重定向脚本的输出到文件中。覆盖文件原有的内容
command &> filename   #重定向stdout(标准输出)和stderr(标准错误)到文件中
command >&2   #重定向command的stdout到stderr
scriptname >> filename   #重定向脚本的输出到文件中。添加到文件末尾,如果没有文件就新创建。

23

| 管道。分析前边命令的输出,并将输出作为后边命令的输入。这是一种产生命令链的好方法。

cat *.lst | sort | uniq
#合并和排序所有的.lst文件,然后删除所有重复的行

输出的命令传递到脚本中。如:

#!/bin/bash
#uppercase.sh : 修改输出,全部转换为大写
tr 'a-z' 'A-Z'
#字符范围必须被""引用起来,来阻止产生单字符的文件名
exit 0

输送ls -l 的输出到uppercase.sh脚本中

]# ls -l | sh uppercase.sh 
总用量 156736
-RW-R--R-- 1 ROOT ROOT 160480880 6月  19 15:32 MYSQL-COMMUNITY-SERVER-5.7.23-1.EL6.X86_64.RPM
-RW-R--R-- 1 ROOT ROOT      7814 6月  19 15:35 MYSQL-COMMUNITY-SERVER-5.7.23-1.EL6.X86_64.RPM.TEST
-RWXR-XR-X 1 ROOT ROOT       702 6月  19 15:35 TEST.SH
-RW-R--R-- 1 ROOT ROOT       166 6月  19 15:58 UPPERCASE.SH

24

|| 或-逻辑操作。

25

& 后台运行。一个命令后边跟一个&,将表示在后台运行。

Example 3.3 在后台运行一个循环

#!/bin/bash
#background-loop.sh

for i in 1 2 3 4 5 6 7 8 9 10
do
  echo -n "$i"
done&

echo   #这个'echo'某些时候将不会显示

for i in 11 12 13 14 15 16 17 18 19 20
do
  echo -n "$i"
done

echo   #这个'echo'某些时候将不会显示

#期望的输出应该是
#1 2 3 4 5 6 7 8 9 10
#11 12 13 14 15 16 17 18 19 20

#然而实际的结果可能是(我的是这个结果)
#11 12 13 14 15 16 17 18 19 20
#1 2 3 4 5 6 7 8 9 10
#(第2个'echo'没执行,为什么?)

#也可能是
#1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#(第1个'echo'没执行,为什么?)

#非常少见的执行结果,也有可能是:
#11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20

#前台的循环先于后台的执行

exit 0

#试着在echo -n "$i"之后加sleep 1;将看到一些乐趣

注意:在一个脚本内后台运行一个命令,有可能造成这个脚本的挂起,等待一个按键响应。后文中有这个问题的解决办法

26

&& 与-逻辑操作

27

27.1

“-” 选项,前缀。在所有的命令内如果想使用选项参数的话,前边都要加上"-"。

COMMAND -[Option1][Option2][...]

27.2

“-” 用于重定向stdin和stdout

(cd /source/directory && tar -cf - .) | (cd /dest/directory && tar -xpvf -)

#从一个目录移动整个目录树到另一个目录(此脚本只是为了演示'-'重定向的用法,便于理解)
#cd /source/directory   源目录
#&&   与操作,如果cd车成功了,那么就执行下面的命令
#tar -cf - .   'c':创建一个新文档;'f'后面跟'-'指定文件作为stdout;'-'后面的file选项,指明作为stdout的目标文件,并且在当前目录('.')执行
#|   管道
#(...)   一个子shell
#cd /dest/direstory   改变当前目录到目标目录
#&&
#tar -xpvf -   'x'解档,'p'保证所有权和文件属性,'v'发完整消息到stdout,'f'后面跟'-',从stdin读取数据

#注意:'x'是一个命令,'p','v','f'是选项

#更优雅的写法应该是
#cd /dest/direstory
#tar -cf - . |(cd ../dest/direstory;tar -xpvf -)

#当然也可以这样写:
#cp -a /source/directory/* /dest/directory
#   或者:
#cp -a /source/directory/* /source/directory/.[^.]* /dest/directory
#如果/source/directory中有隐藏文件的话。


bunzip2 linux-2.6.13.tar.bz2 | tar -xvf -
#bunzip2 linux-2.6.13.tar.bz2 = linux-2.6.13.tar
#tar -xvf linux-2.6.13.tar

Example 3.4 备份最后一天所有修改的文件

#!/bin/bash
#在一个"tarball"中(经过tar和gzip处理过的文件)备份最后24小时当前目录下所有修改的文件

BACKUPFILE=backup-$(date +%m-%d-%Y)   #在备份文件中嵌入时间
archive=${1:-$BACKUPFILE}   #如果在命令行中没有指定备份文件的文件名,那么将默认使用"backup-MM-DD-YYY.tar.gz"
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#这里我给出一个示例,便于理解${1:-$BACKUPFILE}
#b=${a:-456} #如果a未定义, 则将456做为默认值, a仍保持未定义。
#echo $b   456
#echo $a   空
#b=${a:=789} #如果a未定义, 则将789做为默认值, 并且将a的值设置为789
#echo $b   789
#echo $a   789
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
gzip $archive.tar
echo "Directory $PWD backed up in archive file \"$archive.tar.gz\"."

#如果在发现太多文件的时候,或者是文件名包括空格的时候,将执行失败
#Stephane Chazelas建议使用下面的两种代码之一
#--------------------------------------------------------------------
#find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
#使用gnu版本的find
#
#find . -mtime -1 -type f -exec tar rvf "$archive.tar" {} \;
#对于其他风格的UNIX便于移植,但是比较慢
#--------------------------------------------------------------------

exit 0

27.3

“-” 之前工作的目录。"cd -“将回到之前的工作目录,具体参考”$OLDPWD"环境变量。

27.4

“-” 算术减号

28

“=” 算术等号

29

29.1

“+” 算术加号,也用在正则表达式中。

29.2

“+” 选项,对于特定的命令来说,使用"+“来打开特定的选项,用”-"关闭特定的选项。

30

“%” 算术取模运算。也用在正则表达式中。

31

31.1

~ home目录。相当于$HOME变量。~bozo是bozo的home目录。

31.2

~+ 当前工作目录,相当于$PWD变量。

31.3

~- 之前的工作目录,相当于$OLDPWD内部变量。

31.4

=~ 用于正则表达式,这个操作将在正则表达式匹配部分讲解,只有versio3才支持

32

^ 行首,正则表达式中表示行首。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值