高级Bash脚本编程指南(2):Shell特殊字符

本文深入解析Shell编程中特殊字符的应用及高级技巧,包括注释、命令分隔、匹配字符、变量替换、目录分隔、逗号操作、`后置引用、`:操作符等,通过实例讲解其在Shell脚本中的实际应用。

高级Bash脚本编程指南(2):Shell特殊字符

成于坚持,败于止步

# 注释

1. 表示注释

2. 在引号中间和\#等表示#本身

3.echo ${PATH#*:} # 参数替换,不是一个注释

4.echo $(( 2#101011 )) # 数制转换,不是一个注释

; 分隔

1.命令分隔,在一行中写多个命令 echo "aa" ; echo "bb"

2.在条件中的if和then如果放在同一行,也用;分隔

;; case条件的结束

1.命令分隔,在一行中写多个命令 echo "aa" ; echo "bb"

2.在条件中的if和then如果放在同一行,也用;分隔

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."
. 命令相当于

1.命令:source

2.文件名的前缀,隐藏文件

3.目录:.当前目录,..父目录

4.正则表达式:匹配任意单个字符

首先,先举例说明一下"."作为source使用的实例

#!/bin/bash
. data-file # 加载一个数据文件.
# 与"source data-file"效果相同, 但是更具可移植性.
# 文件"data-file"必须存在于当前工作目录, 因为这个文件是使用'basename'来引用的. 
echo "variable1 = $variable1"
echo "variable3 = $variable3"
let "sum = $variable2 + $variable4"
echo "sum = $sum"
exit 0
上面是编写的data_file脚本,通过 . data-file引入,相当于c语言中的include data-file,我们看看data-file的内容
# 这是需要被脚本加载的数据文件.
# 这种文件可以包含变量, 函数, 等等.
# 在脚本中可以通过'source'或者'.'命令来加载. 
# 让我们初始化一些变量.
variable1=22
variable2=474
variable3=5
variable4=97
message1="Hello, how are you?"
message2="Enough for now. Goodbye."
接下来我们看看脚本的执行结果:
root@ubuntu:~/resource/study/shell_study# chmod 777 include_file 
root@ubuntu:~/resource/study/shell_study# ls
clear_log data-file include_file show_self
root@ubuntu:~/resource/study/shell_study# ./include_file 
variable1 = 22
variable3 = 5
sum = 571

上面的结果已经很有力的说明了我们想要的结论

.作为隐藏文件时,建立隐藏文件的方法:touch .data-file

.作为匹配字符说明如下:ab. 可以表示ab+任意字符,处理换行,并且必须是一个字符ab.不能表示ab

"" 部分引用 支持通配符扩展

"STRING"将会阻止(解释)STRING中大部分特殊的字符

' ‘ 全引用,不进行通配符扩展

'STRING'将会阻止STRING中所有特殊字符的解释. 这是一种比使用"更强烈的形式

\ 转义

\X将会"转义"字符X. 这等价于"X", 也等价于'X'. \通常用来转义"和', 这样双引号和单引号就不会被解释成特殊含义了.

/ 目录分隔符

分隔文件名不同的部分(比如 /home/bozo/projects/Makefile).也可以用来作为除法算术操作符.

, 多个命令都被执行,但返回最后一个

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

let "t2 = ((a = 9, 15 / 3))" # Set "a = 9" and "t2 = 15 / 3"

逗号之前会运算,但是只有最后一项被返回

` 后置引用

`command`结构可以将命令的输出赋值到一个变量中去.
cd $LOG_DIR
if [ `pwd` != "$LOG_DIR" ]
then
echo "Can't change to $LOG_DIR"
exit $E_XCD
fi
这里例子是最有力的的说明,在上一章中只不过没有到这个方法,这里pwd命令会返回当前路径,然后与LOG_DIR进行比较,同样你可以定义一个变量保存pwd返回的内容,比如:

path=`pwd`

: 操作符

1.空操作,等价于"NOP" (no op, 一个什么也不干的命令). 

2.死循环: while :,可以被认为与shell的内建命令,与true作用相同.

while :
do
operation-1
operation-2
...
operation-n
done
# 与下边相同:
# while true
# do
# ...
# done

3.在if/then中表示什么都不做,引出分支

if condition
then : # 什么都不做,引出分支. 
else
take-some-action
fi
4.设置默认参数 : ${username=`whoami`}

: ${username=`whoami`}
# ${username=`whoami`} 如果没有开头的":"的话, 将会给出一个错误, 除非"username"是一个命令或者内建命令
5.变量替换 : ${HOSTNAME?} ${USER?} ${MAIL?}
: ${HOSTNAME?} ${USER?} ${MAIL?}
# 如果一个或多个必要的环境变量没被设置的话, 就打印错误信息. 

6.在和 > (重定向操作符)结合使用时,把一个文件截断到0 长度,没有修改它的权限;如果文件在之前并不存在,那么就创建它.如: 

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

7.可能用来作为注释行, 虽然我们不推荐这么做. 使用#来注释的话, 将关闭剩余行的错误检查, 所以可以在注释行中写任何东西. 然而, 使用:的话将不会这样.

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

8.":"还用来在/etc/passwd和$PATH变量中做分隔符.
bash$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games
* 匹配0个或多个字符;数学乘法;**幂运算
root@ubuntu:~/resource/study/shell_study# ls
clear_log data-file include_file show_self
root@ubuntu:~/resource/study/shell_study# echo *
clear_log data-file include_file show_self

? 匹配任意一个字符;但在((a>b?a:b))表示c语言中的三目运算

(( t = a<45?7:11 )) # C语言风格的三元操作.

$ 字符

1.取变量的值 echo $PATH

var1=5
var2=23skidoo
echo $var1 # 5
echo $var2 # 23skidoo

2.正则表达式中表示行的结尾

在正则表达式中, "$"表示行结束符,先分析一下下面的例子吧

root@ubuntu:~/resource/study/shell_study# echo slfjalj$fdjgl
slfjalj

3.${} 参数替换 ${PAHT}

#!/bin/bash
# param-sub.sh
# 一个变量是否被声明或设置,
#+ 将会影响这个变量是否使用默认值, 
#+ 即使这个变量值为空(null).

username0=
echo "username0 has been declared, but is set to null."
echo "username0 = ${username0-`whoami`}"这里定义了username0且初始化是null,所以这里不会有输出,这里的“-”相当于“=”
# 不会有输出.

echo

echo username1 has not been declared.
echo "username1 = ${username1-`whoami`}"这里username1在上面没有定义并初始化为null,所以会显示
# 将会输出默认值.

username2=
echo "username2 has been declared, but is set to null."
echo "username2 = ${username2:-`whoami`}"这里上面初始化了username2并初始化为null,但是这里有个“:”
# ^
# 会输出, 因为:-会比-多一个条件测试.
# 可以与上边的例子比较一下.

# 再来一个:

variable=
# 变量已经被声明, 但是设为空值. 

echo "${variable-0}" # (没有输出)
echo "${variable:-1}" # 1
# ^

unset variable

echo "${variable-2}" # 2
echo "${variable:-3}" # 3

exit 0
我们也看看他的执行结果:
root@ubuntu:~/resource/study/shell_study# chmod 777 para_sub 
root@ubuntu:~/resource/study/shell_study# ls
clear_log data-file include_file para_sub show_self
root@ubuntu:~/resource/study/shell_study# ./para_sub 
username0 has been declared, but is set to null.
username0 = 

username1 has not been declared.
username1 = root
username2 has been declared, but is set to null.
username2 = root ^

1
2
3

4.$* 所有参数

5.$# 参数个数

6.$$ 进程的ID

7.$? 进程的返回状态

( )字符

1.命令组,在一个子Shell中运行 (a=3;echo $a) 其中定义的变量在后面不可用

在括号中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的. 父进程, 也就是脚本本身, 将不能够读取在子进程中创建的变量, 也就是在子shell中创建的变量.
a=123
( a=321; )	

echo "a = $a" # a = 123
# 在圆括号中a变量, 更像是一个局部变量.

2.数组初始化: array=(a,b,c)

{}大括号扩展

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


cp file22.{txt,backup}
# 拷贝"file22.txt"到"file22.b
{ } 代码块,即一个匿名函数,但其中定义的变量在后面依然可用
#!/bin/bash
# 从/etc/fstab中读行.
File=/etc/fstab
{
read line1
read line2
read line3
} < $File

echo "First line in $File is:"
echo "$line1"
echo
echo "Second line in $File is:"
echo "$line2"
echo
echo "third line in $File is:"
echo "$line3"
exit 0
执行结果:
root@ubuntu:~/resource/study/shell_study# ./test1 
First line in /etc/fstab is:
# /etc/fstab: static file system information.

Second line in /etc/fstab is:
#

third line in /etc/fstab is:
# Use 'blkid -o value -s UUID' to print the universally unique identifier
接下来看一个例子:
#!/bin/bash

{ 
echo "Just for a test:"
echo `pwd`
echo "Test end"
} > "test-context" # 把代码块中的所有输出都重定向到文件中.

echo "Results of rpm test in test-context"
exit 0
看看运行结果:
root@ubuntu:~/resource/study/shell_study# chmod 777 test2 
root@ubuntu:~/resource/study/shell_study# ./test2 
Results of rpm test in test-context
root@ubuntu:~/resource/study/shell_study# ls
clear_log include_file show_self test2
data-file para_sub test1 test-context
root@ubuntu:~/resource/study/shell_study# cat test-context 
Just for a test:
/root/resource/study/shell_study
Test end
{ } \; 用在find的-exec中 $find -name *.txt -exec cat {} \;

[ ] 字符

1.测试 [-z $1]

2.数组元素 a[1]='test'

3.[[]]表示测试 使用[[ ... ]]条件判断结构, 而不是[ ... ], 能够防止脚本中的许多逻辑错误. 比如, &&, ||, <, 和> 操作符能够正常存在于[[ ]]条件判断结构中, 但是如果出现在[ ]结构中的话, 会报错.

4.(( ))数学运算

5.在正则表达式中表示范围 [a-z]

< << > 重定向和进程替换 ls -al > a.txt

scriptname >filename 重定向scriptname的输出到文件filename中. 如果filename存在的话, 那么将会被覆盖.

command &>filename 重定向command的stdout和stderr到filename中.

command >&2 重定向command的stdout到stderr中.

scriptname >>filename 把scriptname的输出追加到文件filename中. 如果filename不存在的话, 将会被创建.

[i]<>filename 打开文件filename用来读写, 并且分配文件描述符i给这个文件. 如果filename不存在, 这个文件将会被创建.

> < 还用在ASCII比较 if [[ "$veg1" < "$veg2" ]]

\<,\> 正则表达式中的单词边界.如:bash$grep '\<the\>' textfile

| 管道

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

echo ls -l | sh # 传递"echo ls -l"的输出到shell中,与一个简单的"ls -l"结果相同.

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

管道是进程间通讯的一个典型办法, 将一个进程的stdout放到另一个进程的stdin中. 标准的方法是将一个一般命令的输出, 比如cat或者echo, 传递到一个 "过滤命令"(在这个过滤命令中将处理输入)中, 然后得到结果.

cat $filename1 $filename2 | grep $search_word

当然输出的命令也可以传递到脚本中.

#!/bin/bash
# uppercase.sh : 修改输入, 全部转换为大写.
tr 'a-z' 'A-Z'
# 字符范围必须被""引用起来来阻止产生单字符的文件名.
exit 0
现在让我们输送ls -l的输出到一个脚本中.
bash$ ls -l | ./uppercase.sh
-RW-RW-R-- 1 BOZO BOZO 109 APR 7 19:49 1.TXT
-RW-RW-R-- 1 BOZO BOZO 109 APR 14 16:48 2.TXT
-RW-R--R-- 1 BOZO BOZO 725 APR 20 20:56 DATA-FILE

管道中的每个进程的stdout比须被下一个进程作为stdin来读入. 否则, 数据流会阻塞, 并且管道将产生一些非预期的行为.

cat file1 file2 | ls -l | sort# 从"cat file1 file2"中的输出并没出现.

作为子进程的运行的管道, 不能够改变脚本的变量.

variable="initial_value"

echo "new_value" | read variable

echo "variable = $variable" # variable = initial_value

如果管道中的某个命令产生了一个异常,并中途失败,那么这个管道将过早的终止. 这种行为被叫做broken pipe, 并且这种状态下将发送一个SIGPIPE 信号.

>| 强制重定向(即使设置了noclobber 选项--就是-C 选项).这将强制的覆盖一个现存文件.

|| 逻辑或操作 ;用在两个命令之间的时候,表示在前一个命令结束时,若返回值为 false,继续执行下一个命令

&& 逻辑与;用在两个命令之间的时候,表示在前一个命令结束时,若返回值为 true,继续执行下一个命令

& 后台运行
看一个例子
#!/bin/bash
# background-loop.sh
for i in 1 2 3 4 5 6 7 8 9 10 # 第一个循环.
do	
echo
echo -n "$i "
done & # 在后台运行这个循环.

# 在第2个循环之后, 将在某些时候执行. 
echo # 这个'echo'某些时候将不会显示. 
for i in 11 12 13 14 15 16 17 18 19 20 # 第二个循环.
do
echo -n "$i "
done 
echo # 这个'echo'某些时候将不会显示. 
exit 0
看一下结果:
root@ubuntu:~/resource/study/shell_study# ./for_test 

11 12 13 14 15 16 17 18 19 20 
root@ubuntu:~/resource/study/shell_study# 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 

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

1.参数选项

2. 减号

3. 重定向stdin和stdout:cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)

4.先前的工作目录 cd -

5.注:使用-开头的文件名和变量名可能会出现一些问题

+ 一个命令或者过滤器的选项标记.

~ home目录

~+ 当前工作目录

~- 先前工作目录

^ 正则表达式中表示行首

其中命令的很多细节并没有研究的很彻底,好多好碎,以后见到用到具体的命令再具体分析吧

先到这里了,O(∩_∩)O~

我的专栏地址:http://blog.csdn.net/column/details/shell-daily-study.html

待续。。。。

shell高级编程,中英文合集,均为高清版 目录如下: 第一部分. 热身 1. 为什么使用shell 编程 2. 带着一个Sha-Bang 出发(Sha-Bang 指的是#!) 2.1. 调用一个脚本 2.2. 初步的练习 第二部分. 基本 3. 特殊字符 4. 变量和参数的介绍 4.1. 变量替换 4.2. 变量赋值 4.3. Bash 变量是不分类型的 4.4. 特殊的变量类型 5. 引用(翻译的可能有问题,特指引号) 5.1. 引用变量 5.2. 转义(\) 6. 退出和退出状态 7. Tests 7.1. Test 结构 7.2. 文件测试操作 7.3. 其他比较操作 7.4. 嵌套的if/then 条件test 7.5. 检查你的test 知识 8. 操作符和相关的主题 8.1. 操作符 8.2. 数字常量 第三部分. 超越基本 9. 变量重游 9.1. 内部变量 9.2. 操作字符串 9.3. 参数替换 9.4. 指定类型的变量:declare 或者typeset 9.5. 变量的间接引用 9.6. $RANDOM: 产生随机整数 9.7. 双圆括号结构 10. 循环和分支 10.1. 循环 10.2. 嵌套循环 10.3. 循环控制 10.4. 测试与分支(case 和select 结构) 11. 内部命令与内建 11.1. 作业控制命令 12. 外部过滤器,程序和命令 12.1. 基本命令 12.2. 复杂命令 12.3. 时间/日期命令 12.4. 文本处理命令 12.5. 文件与归档命令 12.6. 通讯命令 12.7. 终端控制命令 12.8. 数学计算命令 12.9. 混杂命令 13. 系统与管理命令 13.1. 分析一个系统脚本 14. 命令替换 15. 算术扩展 16. I/O 重定向 16.1. 使用exec 16.2. 代码块的重定向 16.3. 应用 17. Here Documents 17.1. Here Strings 18. 休息时间 Part 4. 高级 19. 正则表达式 19.1. 一个简要的正则表达式介绍 19.2. 通配 20. 子shell(Subshells) 21. 受限shell(Restricted Shells) 22. 进程替换 23. 函数 23.1. 复杂函数和函数复杂性 23.2. 局部变量 23.3. 不使用局部变量的递归 24. 别名(Aliases) 25. 列表结构 26. 数组 27. /dev 和 /proc 27.1. /dev 27.2. /proc 28. 关于Zeros 和Nulls 29. 调试 30. 选项 31. Gotchas 32. 脚本编程风格 32.1. 非官方的Shell 脚本风格 33. 杂项 33.1. 交互式和非交互式的shells 和脚本 33.2. Shell 包装 33.3. 测试和比较: 另一种方法 33.4. 递归 33.5. 彩色脚本 33.6. 优化 33.7. 各种小技巧 33.8. 安全话题 33.8.1. 被感染的脚本 33.8.2. 隐藏Shell 脚本源码 33.9. 移植话题 33.10. 在Windows 下进行Shell 编程 34. Bash, 版本 2 和 3 34.1. Bash, 版本2 34.2. Bash, 版本3 35. 后记 35.1. 作者后记 35.2. 关于作者 35.3. 哪里可以取得帮助? 35.4. 制作这本书的工具 35.4.1. 硬件 35.4.2. 软件和排版软件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值