高级Bash脚本编程指南(2):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 L O G D I R i f [ ‘ p w d ‘ ! = " LOG_DIR if [ `pwd` != " LOGDIRif[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和 P A T H 变 量 中 做 分 隔 符 . b a s h PATH变量中做分隔符. bash 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 f d j g l s l f j a l j 3. fdjgl slfjalj 3. fdjglslfjalj3.{} 参数替换 ${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 “KaTeX parse error: Expected 'EOF', got '#' at position 15: {variable-0}" #̲ (没有输出) echo "{variable:-1}” # 1

^

unset variable

echo “KaTeX parse error: Expected 'EOF', got '#' at position 15: {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 F i l e i s : &quot; e c h o &quot; File is:&quot; echo &quot; Fileis:"echo"line1”
echo
echo “Second line in F i l e i s : &quot; e c h o &quot; File is:&quot; echo &quot; Fileis:"echo"line2”
echo
echo “third line in F i l e i s : &quot; e c h o &quot; File is:&quot; echo &quot; Fileis:"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 [[ “ v e g 1 &quot; &lt; &quot; veg1&quot; &lt; &quot; 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.参数选项

  1. 减号

  2. 重定向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

待续。。。。

作者:Ela–学海无涯
来源:CSDN
原文:https://blog.csdn.net/xinyuwuxian/article/details/8835697
版权声明:本文为博主原创文章,转载请附上博文链接!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值