Shell 变量替换

(0 comments)

变量赋值

赋值

在设定变量的时侯,得遵守如下规则:

  • 等号左右两边不能使用区隔符号(IFS),也应避免使用 shell 的保留字符(meta charactor)。
  • 变量名称不能使用 $ 符号。
  • 由字母和下划线组成,并且变量名称的第一个字母不能是数字(number)。
  • 变量名称长度不可超过 256 个字母。
  • 变量名称及变量值之大小写是敏感的(case sensitive)

给变量NAME赋值为ice_wal,例如:

NAME=ice_walk 

在应用变量NAME的时候,在NAME前加”$”即可。例如:

echo $NAME

字符串长度

计算字符串长度

str="abc123"
echo ${#str}

扩充变量值

除了shell中的meta,其它的[^a-zA-Z0-9_]几乎都可以作单词边界。这些功能有时候会在程序中有意想不到的作用。例如:

$ a=bcd
$ echo $a.php 得:bcd.php
$ echo $a%b 得:bcd%b
$ echo /$a/bc 得:/bcd/bc

对于shell中的meta字符,则使用backslash。

$ echo $a\*b 得:bcd*b

用户也可以在命令行上同时对多个变量赋值,赋值语句之间用空格分开:

X=x Y=y (注意变量赋值是从右到左进行的)
X=$Y Y=y X的值是y
X=z Y=$Z Y的值是空 (变量未赋值时,shell不报错,而是赋值为空)

内置命令eval

在要求多于一次字符串求值的情况下可以使用内置命令 eval。例如,如果变量 X 有值 $y, 并且若 y 有值 pqr 则:eval echo $X 将回显字符串 pqr。

一般的,eval 命令求值它的实际参数(与所有命令一样)并把这个结果作为给 shell 的输入来对待。读这个输入并执行作为结果的命令。例如:

wg=\'eval who|grep\'
$wg fred

等价于:

who|grep fred

在这例子中,需要 eval 的原因是替换之后不解释元字符如 |。

局部变量

在 BASH 程序中如果一个变量被使用了,那么直到该程序的结尾,该变量都一直有效。为了使得某个变量存在于一个局部程序块中,就引入了局部变量的概念。BASH 中,在变量首次被赋初值时加上 local 关键字就可以声明一个局部变量。

C-shell的用户应该注意:

C-shell中的赋值不同于Bourne和Korn shell。C-shell使用set命令进行赋值:

$set LOOK = /usr/mydir (注意在等号两边要加空格,好像不加也可以)

就象文件名的置换一样,变量名的置换也是在调用程序前进行。在变量替换中,变量的值取代了变量名。例如在:$ls $LOOK/filename 用 /usr/mydir/filename 做参数调用ls。

变量替换

Shell 之所以强大,其中的一个因素是它可以在命令行中对变量作替换(substitution)处理。在命令行中使用者可以使用 $ 符号加上变量名称(除了在用 = 号定义变量名称之外),将变量值给替换出来,然后再重新组建命令行。

一般情况下,$var 与 ${var} 并没有啥不一样。但是用 ${ } 会比较精确的界定变量名称的范围,比方说:

$ A=B
$ echo $AB

原本是打算先将 $A 的结果替换出来,然后再补一个 B 字母于其后,但在命令行上,真正的结果却是只会提换变量名称为 AB 的值出来。若使用 ${ } 就没问题了。

A. 掐头去尾

假设我们定义了一个变量为:file=/dir1/dir2/dir3/my.file.txt,我们可用 ${ } 分别替换获得不同的值:

${file#*/} 从变量file的字符串左边开始删除字符,直到第一个“/”:dir1/dir2/dir3/my.file.txt 
${file##*/} 从变量file的字符串左边开始删除字符,直到最后一个“/”:my.file.txt 
${file#*.} 从变量file的字符串左边开始删除字符,直到第一个“.”:file.txt 
${file##*.} 从变量file的字符串左边开始删除字符,直到最后一个“.”:txt 
${file%/*} 从右部开始拿掉字符,直到遇到(从右部数起的)第一个“/” :/dir1/dir2/dir3 
${file%%/*} 从右部开始拿掉字符,直到遇到(从右部数起的)最后一个“/”:(空值) 
${file%.*} 从右部开始拿掉字符,直到遇到(从右部数起的)第一个“.”:/dir1/dir2/dir3/my.file 
${file%%.*} 从右部开始拿掉字符,直到遇到(从右部数起的)最后一个“.”:/dir1/dir2/dir3/my

记忆的方法为:

  • # 是去掉左边(在键盘上 # 在 $ 之左边)
  • % 是去掉右边(在键盘上 % 在 $ 之右边)
  • 单一符号是最小匹配﹔两个符号是最大匹配。

同时要注意:在 # 和 % 后可以使用任何shell中的模式匹配。并非仅限于*、.、/。

B. 字串提取

${file:0:5} 提取最左边的 5 个字节:/dir1 
${file:3} 去掉前面3个字符 
${file:5:5} 提取第 5 个字节右边的连续 5 个字节:/dir2

C. 字符串替换

${file/dir/path} 将第一个 dir 提换为 path:/path1/dir2/dir3/my.file.txt 
${file//dir/path}将全部 dir 提换为 path:/path1/path2/path3/my.file.txt

不可以使用 regexp , 只能用shell中 * 、? 的文件扩展方式。

D. 变量名通配符

${!prefix*} 代表所有以prefix开始的变量名,各变量名称之间以空格符分隔 $ ab=1 abc=2 abcd=3 $ echo ${!ab*} 输出:ab abc abcd

E. 变量赋值(没设定、空值、非空值)

${#myvar} 计算出变量值的长度 
${myvar=default} 若 $myvar 没设定,则输出并取值 default。(保留空值及非空值) 
${myvar:=default} 若 $myvar 没设定或为空值,则输出并取值 default。(保留非空值) 
${myvar+default} 若 $myvar 设定为空值或非空值,输出但不取值 default,否则返回空(null)。 
${myvar:+default} 若 $myvar 设定为非空值,输出但不取值 default,否则返回空(null)。 
${myvar-default} 若 $myvar 没设定,输出但不取值 default。(保留空值及非空值) 
${myvar:-default} 若 $myvar 没设定或为空值,输出但不取值 default。(保留非空值) 
${myvar?default} 若 $myvar 没设定,将 default 输出至 STDERR。(不取值,保留空值及非空值) 
${myvar:?default} 若 $myvar 没设定或为空值,将 default 输出至STDERR。(不取值,保留非空值)

变量替换的值也可以是` `括起来的命令:$USERDIR={$Mydir:-`pwd`}

${var:?message} 当没有指定message时,shell将显示一条默认的消息,例如:$UNAME=

$echo ${UNAME:?} 结果显示:sh:UNAME:parameter null or not set

速记法:

n = 慈善家(你没有才给你) 
n + 商人(你有才给换) 
n - 假慈善家(表明上才给) 
n ? 牛皮精(只说不做) 
n 加“:”后,相当于把“空值”也当作“unset”值处理,对有值的变量则无影响

F. bash 的组数(array)处理

一般而言,A=“a b c def” 这样的变量只是将 $A 替换为一个单一的字符串,但是改为 A=(a b c def) ,则是将 $A 定义为组数。bash 的组数替换方法可参考如下方法:

${A[@]} 或 ${A[*]} 可得到 a b c def (全部组数); 
${A[0]} 可得到 a (第一个组数),${A[1]} 则为第二个组数; 
${#A[@]} 或 ${#A[*]} 可得到 4 (全部组数数量); 
${#A[0]} 可得到 1 (即第一个组数(a)的长度),${A[3]} 可得到 3 (第一个组数(def)的长度); 
A[3]=xyz 则是将第 4 个组数重新定义为 xyz。

清除赋值

变量一旦经过 unset 取消之后,其结果是将整个变量拿掉,而不仅是取消其变量值。如下两行其实是很不一样的:

$ A=
$ unset A

第一行只是将变量 A 设定为”空值”(null value),但第二行则让变量 A 不在存在:

$ A=
$ echo $A 打印输出为空
$ unset A
$ echo $A 打印输出为空

请务必能识别 null value 与 unset 的本质区别,这在一些进阶的变量处理上是很严格的。与export一样,unset 命令行也同样会作变量替换(这其实就是 shell 的功能之一),因此:

A=B
B=C

unset $A 事实上所取消的变量是 B 而不是 A 。

$( )、` `、$(( ))、(( ))

A. $( )与 ` `(反引号)

都是用来做命令替换用(command substitution)的。所谓的命令替换与前面的变量替换差不多,都是用来重组命令行:完成引号里的命令行,然后将其结果替换出来,再重组命令行。

例如:$ echo last sunday is $(date -d “last sunday” +%Y-%m-%d) ,如此便可得到上一星期天的日期了。

在操作上,用 $( ) 或 ` ` 都无所谓,只是我”个人”比较喜欢用 $( ) ,理由是:

(1) ` ` 很容易与 ' ' ( 单引号)搞混乱。有时在一些奇怪的字形显示中,两种符号是一模一样的(直竖两点)。 
(2) 在多层次的复合替换中,` ` 须要额外的跳脱( \` )处理,而 $( ) 则比较直观。

例如:command1 `command2 `command3` `这是错的。

原本的意图是要在 command2 `command3` 先将 command3 提换出来给command 2 处理,然后再将结果传给 command1 `command2 …` 来处理。然而,真正的结果在命令行中却是分成了 `command2 ` 与 `` 两段。

正确的输入应该是:command1 `command2 \`command3\` `

要不然,换成 $( ) 就没问题了:command1 $(command2 $(command3))

不过,$( ) 并不是没有弊端的, ` ` 基本上可用在全部的 unix shell 中使用,若写成 shell script ,其移植性比较高。而 $( ) 并不见的每一种 shell 都能使用,只能说,若你用 bash2 的话,肯定没问题!

例:通过$()实现对某目录下文件进行操作的功能:

#!/bin/bash
j=$(ls $1)
for k in $j
do
echo "$k"
if [ $k = text ]
then
cat $k
else
echo no text
fi
done

B. $(( ))用来作整数运算

在 bash 中,$(( )) 的整数运算符号大致有这些:

+ - * / :分别为 “加、减、乘、除”。 
% :余数运算 
& | ^ !:分别为 “AND、OR、XOR、NOT” 运算。

例:

$ a=5; b=7; c=2
$ echo $(( a+b*c )) #得19
$ echo $(( (a+b)/c )) #得6
$ echo $(( (a*b)%c)) #得1

在 $(( )) 中的变量,可用 $ 符号来替换,也可以不用,如:$(( $a + $b * $c)) 也可得到 19 的结果。此外,$(( )) 还可作不同进位(如二进制、八进位、十六进制)作运算呢,只是,输出结果皆为十进制而已:

echo $((16#2a)) 结果为 42 (16进位转十进制)

以一个实用的例子来看看吧,假如当前的 umask 是 022 ,那么新建文件的权限即为:

$ umask 022
$ echo "obase=8;$(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc
644

C. 用 (( )) 重定义变量值或作testing

a=5; ((a++)) 可将 $a 重定义为 6 
a=5; ((a--)) 则为 a=4 
a=5; b=7; ((a < b)) 会得到 0 (true) 的返回值。

常见的用于 (( )) 的测试符号有如下这些:

<:小于 
>:大于 
⇐:小于或等于 
>=:大于或等于 
==:等于 
!=:不等于

不过,使用 (( )) 作整数测试时,请不要跟 [ ] 的整数测试搞混乱了。

Currently unrated

Comments

There are currently no comments

New Comment

required

required (not published)

optional

required