Shell 符号展开

shell中有7中展开。展开的顺序是:大括号展开;波浪展开、参数和变量展开、算术展开和命令替换(以从左到右的方式完成);分词;和文件名扩展。

在支持它的系统上,还有一种额外的扩展:进程替换。这与波浪线、参数、变量、算术展开和命令替换同时执行。

在执行这些扩展之后,原始单词中的引号字符将被删除,除非它们本身被引号括起来(引号删除)。

只有大括号展开、分词和文件名展开才能增加展开的单词数;其他扩展将单个单词扩展为单个单词。唯一的例外是$ @和$ ∗ 以 及 *以及 {name[@]}和${name[*]}(见数组)的扩展。

大括号展开

大括号展开是一种可以生成任意字符串的机制。展开大括号的模式采用可选的前缀,后接一系列逗号分隔的字符串或一对大括号之间的序列表达式,后接可选的后缀。前缀与大括号中包含的每个字符串组合,然后将后缀附加到每个结果字符串,从左到右展开。

大括号展开可以嵌套。每个扩展字符串的结果都没有排序;从左到右的顺序保持不变。例如:

echo a{d,c,b}e
ade ace abe

序列表达式的形式为

{x…y[…Incr]}

其中x和y是整数或单个字符,Incr是可选的增量,是一个整数。当提供整数时,表达式扩展为x和y之间的每个数字(包括x和y)。

echo {1..10}
1 2 3 4 5 6 7 9 10

提供的整数可以加上’ 0 '前缀,以强制每个项具有相同的宽度。当x或y以0开头时,shell会试图强制所有生成的术语包含相同数量的数字,必要时还会进行零填充。

echo {01..10} 
01 02 03 04 05 06 07 08 09 10

echo {0001..10} 
0001 0002 0003 0004 0005 0006 0007 0008 0009 0010 

当提供字符时,表达式使用默认的C语言环境,按字典顺序展开到x和y之间的每个字符(含)。

echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

注意 :x和y必须是相同的类型。当提供增量时,它被用作每个项之间的差值。默认的增量是1或-1。

echo {z..a..-1}
z y x w v u t s r q p o n m l k j i h g f e d c b a
echo {1..10..2} 
1 3 5 7 9 

大括号展开在任何其他展开之前执行,任何其他展开的特殊字符都保留在结果中。它是严格的文本。Bash不会对展开上下文或大括号之间的文本应用任何语法解释。

正确格式的大括号展开必须包含不带引号的前括号和后括号,以及至少一个不带引号的逗号或有效的序列表达式。任何不正确形成的大括号展开都保持不变。

一个{或’,‘可以用反斜杠括起来,以防止被认为是大括号表达式的一部分。为了避免与参数展开冲突,字符串’ ${‘被认为不符合大括号展开的条件,并禁止大括号展开,直到结束的’}’。

当要生成的字符串的公共前缀比上面的例子中要长时,这个结构通常被用作一种简写:

mkdir  -p /shelldir/{test,test1,test2}
或者
chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}

波浪线扩展

如果一个单词以未加引号的斜杠字符(’ ~ ')开头,则第一个未加引号的斜杠之前的所有字符(如果没有加引号的斜杠,则所有字符)都被视为波浪前缀。如果波浪号前缀中的字符都没有被引用,那么波浪号后面的波浪号前缀中的字符将被视为可能的登录名。如果这个登录名是空字符串,则用shell变量HOME的值替换波浪号。如果没有设置HOME,则替换执行shell的用户的HOME目录。否则,波浪前缀将被替换为与指定登录名关联的主目录。

echo ~
/root

echo ~/
/root/

如果波浪前缀是’ ~+ ',则shell变量pwd的值将替换波浪前缀。

echo ~+
等价于
pwd

如果波浪符号前缀是’ ~- ',则shell变量oldpwd的值(如果它被设置了)将被替换。

ehco ~-
退回到上次目录

如果在波浪号前缀中,波浪号后面的字符由数字N组成,可选的前缀是’ + ‘或’ - ',波浪号前缀将被替换为目录堆栈中的相应元素,因为它会被内部调用的dirs显示,在波浪符号前缀中,波浪符号后面的字符作为参数(参见目录堆栈)。如果波浪线前缀(无波浪线)由一个没有“+”或“-”前导的数字组成,则假定为“+”。

echo ~0
/root

如果登录名不合法,或者波浪号扩展失败,则不做任何替换。

echo ~sadfsdaf
~sadfsdaf

每个变量的赋值都要检查紧跟在’:‘或第一个’ = '后面的未加引号的波浪前缀。在这些情况下,也会执行波浪展开。

name=~
echo $name 
/root

name=abc:~ 
echo $name 
abc:/root

name=abc:~:~ 
echo $name 
abc:/root:/root

name=abc~ 
echo $name 
abc~ 

因此,可以在给PATH、MAILPATH和CDPATH赋值时使用带有波浪号的文件名,并且shell会赋值扩展值。

参数展开

$字符用于参数展开、命令替换或算术展开。要展开的参数名或符号可以用大括号括起来,大括号是可选的,但用于保护要展开的变量不受紧跟其后的字符的影响,这些字符可能被解释为名称的一部分。

当使用大括号时,匹配的结束大括号是第一个不通过反斜杠转义的’}’,也不包含在引号中的字符串中,也不包含在嵌入式算术展开、命令替换或参数展开中。

参数展开的基本形式是${parameter}。参数的值被替换。参数是上面描述的shell参数或数组引用。当参数是一个具有多个数字的位置参数,或者参数后面跟一个字符,而该字符不能被解释为其名称的一部分时,需要使用大括号。

name="test" 

echo $name 
test


echo abc$namedef 
abc

echo abc${name}def 
abctestdef 

如果参数的第一个字符是感叹号(!),而参数不是name,它将引入一个间接级别。Bash使用扩展参数的其余部分形成的值作为新参数;然后展开该参数,并在展开的其余部分使用该值,而不是原始参数的展开部分。这就是所谓的间接扩展。可以进行波浪展开、参数展开、命令替换、算术展开。

name=test
n=name
echo ${!n}
test

如果参数是一个变量名引用,这将扩展为参数引用的变量名,而不是执行完全的间接扩展。例外情况是$ {!prefix*}和$ {!name[@]}。感叹号必须紧跟在左大括号后面,以便引入间接形式。

在下面的每个例子中,word都要进行波浪展开、参数展开、命令替换和算术展开。

当不执行子字符串展开时,使用下面描述的形式(例如,’:- '),Bash测试参数是否未设置或为空。省略冒号只会对未设置的参数进行测试。换句话说,如果包含冒号,操作符将测试参数是否存在,以及参数值是否不为空;如果省略冒号,则操作符只测试是否存在。
${parameter:-word}

如果parameter未设置或为空,则word的展开将被替换。否则,参数的值将被替换。

echo ${name:-word}
word
name=test
echo ${name:-word}
test

如果parameter未设置或为空,则将word的展开部分赋给parameter。然后替换parameter的值。位置参数和特殊参数不能以这种方式赋值。

echo ${name:=test}
test
echo $name
test

如果parameter为空或未设置,则word的展开(或word不存在时的展开消息)被写入标准错误,如果shell不是交互式的,则退出。

name=test
echo ${name:?}
test

echo ${a:?}
-bash: a: parameter null or not set

echo ${a:? is not set}
-bash: a:  is not set

如果parameter为空或未设置,则不替换任何内容,与-号相反,否则替换word的展开。

echo ${name:+word}
word

echo ${a:+word}
输出为空

子字符串扩展

${parameter:offset}
${parameter:offset:length}

类似于python 的切片,它获取长度为length的参数值的字符,从offset位置开始。如果省略了length,则它展开为parameter值的子字符串,从offset指定的字符开始,一直扩展到值的末尾。长度和偏移量是算术表达式。如果offset计算值为小于0的数字,则该值将用作从parameter值结束开始的字符偏移量。如果length求值为一个小于0的数字,则它将被解释为从parameter值的末尾开始的字符偏移量,而不是字符数,并且展开是offset和结果之间的字符数。
注意:负偏移量必须与冒号之间至少有一个空格隔开,以避免与’:- '展开形式混淆。

举例来说明参数和下标数组的子字符串扩展:

string=01234567890abcdefgh 

echo ${string:7} 
7890abcdefgh

echo ${string:7:2} 
78 

echo ${string:7:-2} 
7890abcdef

echo ${string: -7} 
bcdefgh  

echo ${string: -7:2}
bc

如果参数为’ @ ‘,则为下标为’ @ ‘或’ * ‘的索引数组,或关联数组名,则结果不同,如下所述。如果parameter为’ @ ',则结果为从offset开始的length个位置参数。一个负的偏移量是相对与最后一个位置参数的,所以-1表示最后一个参数,-2表示倒数第二个参数。如果length计算值为小于零的数字,则为展开错误。

举例:
使用位置参数进行子字符串扩展:

set -- 1 2 3 4 5 6 7 8 9 0 a b c d e f g h
echo ${@:7} 
7 8 9 0 a b c d e f g h

echo ${@:7:2} 
7 8 

echo ${@:7:-2} 
-2: substring expression < 0 

echo ${@: -7:2} 
b c 

echo ${@:0} 
-bash 1 2 3 4 5 6 7 8 9 0 a b c d e f g h 

echo ${@:0:2} 
-bash 1

如果parameter是一个下标为’ @ ‘或’ * '的索引数组名,则结果为以${parameter[offset]}开头的数组的length个成员。偏移量为负数表示相对于最后一个元素,length为负数贼展开错误。下面的例子展示了如何在索引数组中使用子字符串扩展:

array=(0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h)

echo ${array[@]:7}
7 8 9 0 a b c d e f g h

echo ${array[@]:7:2}
7 8

echo ${array[@]: -7:2} 
b c

echo ${array[@]: -7:-2}
 bash: -2: substring expression < 0

echo ${array[@]:0}
0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h

echo ${array[@]:0:2}
0 1

echo ${array[@]: -7:0}
输出空

子字符串索引是从零开始的,除非使用了位置参数,在这种情况下,索引默认从1开始。如果offset为0,并且使用了位置参数,则将$0作为列表的前缀。

${!prefix*}
${!prefix@}

扩展为名称以前缀开头的变量名,用IFS特殊变量的第一个字符分隔。当使用’ @ '时,展开出现在双引号内,每个变量名展开成一个单独的单词。

#获取所有定义n开头的变量
n=1
name=test
name1=aa

echo ${!n*}
n name name1

echo ${!n@}
n name name1
${#parameter}

参数展开值的字符长度被替换,获取字符串长度

name=test
echo ${#name}
4

word将被生成为一个匹配模式

${parameter#word}
${parameter##word}

如果模式匹配参数的值的开头部分,如果是#则会把参数的值去掉最小匹配后替换,如果是##则会把参数的值去掉最大匹配后替换。如果参数是’ @ ‘或’ * ‘,模式删除操作将依次应用于每个位置参数,展开是结果列表。如果parameter是一个下标为’ @ ‘或’ * '的数组变量,模式删除操作将依次应用于数组的每个成员,并展开结果列表。

Num=123456
name=test
echo ${name#[0-9]}
test

echo ${Num#1}
23456

echo ${name#t}
est

echo ${name#te}
st
${parameter%word}
${parameter%%word}

如果模式匹配参数的值的结尾部分,如果是%则会把参数的值去掉最小匹配后替换,如果是%%则会把参数的值去掉最大匹配后替换。如果参数是’ @ ‘或’ * ‘,模式删除操作将依次应用于每个位置参数,展开是结果列表。如果parameter是一个下标为’ @ ‘或’ * '的数组变量,模式删除操作将依次应用于数组的每个成员,并展开结果列表。

从末尾开始匹配
name=test

echo ${name%t} 
tes

将参数的值能被pattern匹配的部分替换为string。

如果pattern以’ / '开头,所有匹配的部分将被替换为string。通常只替换第一个匹配项。

name="aaa"
echo ${name/a/i}
iaa
echo ${name//a/i}
iii

如果pattern以’ # ‘开头,它必须匹配parameter扩展值的开头。如果模式以’ % '开头,它必须匹配参数扩展值的末尾。如果string为空,pattern的匹配将被删除,后面的pattern可能会被省略。

name="aaa" 
echo ${name/#a/i} 
iaa

echo ${name/%a/i}
aai

echo ${name/a/} 
aa

echo ${name/a} 
a 

如果启用了nocasematch shell选项,则不管字母字符的大小写,都将执行匹配。如果参数是’ @ ‘或’ * ‘,替换操作将依次应用于每个位置参数,展开是结果列表。如果parameter是一个下标为’ @ ‘或’ * '的数组变量,则替换操作依次应用于数组的每个成员,展开是结果列表。

${parameter^pattern}
${parameter^^pattern}
${parameter,pattern}
${parameter,,pattern}

此展开将修改参数中字母字符的大小写。参数的扩展值中的每个字符都将根据模式进行测试,如果匹配模式,则转换其大小写。模式不应该尝试匹配多个字符。^运算符将匹配模式的小写字母转换为大写字母;,操作符将匹配的大写字母转换为小写字母。和,在展开的扩展值中转换每个匹配的字符; ^和,仅在展开的扩展值中转换第一个字符;

 name=aBaB
 echo ${name^a}
 ABaB
 
 echo ${name,B}
 aBaB
 
 echo ${name^^a}
 ABAB
 
 echo ${name,,B}
 abab

如果pattern被省略,它会被视为’ ?,它匹配任意字符。


name=aBaB
echo ${name^?}
ABaB

echo ${name^^?}
ABAB
 
echo ${name,?}
aBaB

echo ${name,,?}
abab

如果参数为’ @ ‘或’ * ‘,则大小写修改操作依次应用于每个位置参数,展开是结果列表。如果parameter是一个下标为’ @ ‘或’ * '的数组变量,则顺序对数组的每个成员应用大小写修改操作,并展开结果列表。

命令替换

命令替换允许命令的输出替换命令本身。

当命令被如下所示包围时,就会发生命令替换:


$(command)
 
或者

`command`

Bash通过在子shell环境中执行命令,并用命令的标准输出替换命令替换,并删除末尾的换行符来执行扩展。内嵌的换行符不会被删除,但它们可以在分词时被删除。命令替换 ( c a t 文 件 ) 可 以 被 等 价 的 但 更 快 的 (cat文件)可以被等价的但更快的 (cat)(<文件)代替。

echo $(cat test.py)

echo $(< test.py) 

当使用旧式反引号形式的替换时,反斜杠保留其字面意义,除非后面跟着$ ,`,\。 第一个不带反斜杠的反引号结束命令替换。当使用$(command)形式时,括号之间的所有字符组成命令;没有字符得到特别的待遇。

命令替换可以嵌套。使用反引号嵌套时,请用反斜杠转义内部的反引号。

echo $(ls $(pwd))

echo ls \pwd

如果替换出现在双引号内,则不会对结果执行分词和文件名扩展。

 echo "$(ls ~)" # 原样输出不分词
 echo $(ls ~)

算术展开

算术展开允许对算术表达式求值并替换结果。 算术展开的格式为:

$(( expression ))

表达式会被当作双引号内的表达式来处理,但是括号内的双引号不会被特别处理。表达式中的所有标记都要进行参数和变量展开、命令替换和引号删除。结果被视为要求值的算术表达式。算术展开可以嵌套。

计算是根据下面列出的规则执行的(参见Shell算术)。如果表达式无效,Bash将向标准错误输出一条指示失败的消息,并且不会发生替换。

进程替换

进程替换允许引用进程的输入或输出。

它的形式是

 <(list)



(list) 

当使用<(list)形式时,用来产生标准输出,它的输出结果可以作为另一个命令的输入

grep 'aaa' <(ls)  # <(ls)是一个临时文件,文件内容是ls的结果,然后grep在其中查找'aaa'

当使用>(list)形式时,用来接受标准输入,它可以接收另一个命令的输出结果

pwd >(ls)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

久醉绕心弦,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值