Parameter Expansion

-- TOC --

看脚本时,Parameter Expansion感觉是比较难的,但这其实也是bash这种字符串脚本语言的可编程能力的体现。

Parameter Expansion基本就是由一些语法规则组成。

变量测试与赋值

${parameter:-word}

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted. 如果parameter不存在或为null,则扩展为word,否则扩展为parameter的值。相当于在扩展parameter的时候,提供一个默认值。

${parameter:+word}

If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted. 如果parameter不存在或为null,扩展的结果就是null,否则扩展出word。

-+这两条规则刚好相反,可方便的用来判断变量是否存在:

$ echo ${ABC:-0}  # if ABC is not existed
0
$ ABC=123
$ echo ${ABC:+1}  # if ABC is existed
1
${parameter:=word}

If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way. 如果parameter不存在或为null,word赋值给parameter,然后扩展。相当于在扩展parameter的时候,不仅提供默认值,还赋值了!

${parameter:?word}

If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted. 如果parameter不存在或为null,扩展为word,并且是写入没缓存的stderr,否则扩展parameter的值。如果是 not interactive shell,就是在脚本文件中执行,脚本退出。

$ unset ABC
$ echo ${ABC:?}
bash: ABC: parameter null or not set
$ ABC=
$ echo ${ABC:?}
bash: ABC: parameter null or not set
$ echo ${ABC:?what the f...}
bash: ABC: what the f...
$ echo $?
1

这个效果就像只对某一个变量是否存在进行检查,而set -u是对全局所有变量都做检查。

${#parameter}

The length in characters of the expanded value of parameter is substituted. 扩展为parameter值的长度。(与$#特殊变量对应)

$ echo $ABC
123456789
$ echo ${#ABC}
9

substring expansion

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

It expands to up to length characters of the value of parameter starting at the character specified by offset. If length is omitted, it expands to the substring of the value of parameter starting at the character specified by offset and extending to the end of the value. index从0开始,如果没有length,就是到最后。当offset为负数的时候,在冒号后面加一个空格,与前面的一种用法区分开来!

下面是官方文档中的示例:

$ string=01234567890abcdefgh
$ echo ${string:7}
7890abcdefgh
$ echo ${string:7:0}

$ echo ${string:7:2}
78
$ echo ${string:7:-2}
7890abcdef
$ echo ${string: -7}  # must be a space after :
bcdefgh
$ echo ${string: -7:0}

$ echo ${string: -7:2}
bc
$ echo ${string: -7:-2}
bcdef
$ set -- 01234567890abcdefgh
$ echo ${1:7}
7890abcdefgh
$ echo ${1:7:0}

$ echo ${1:7:2}
78
$ echo ${1:7:-2}
7890abcdef
$ echo ${1: -7}
bcdefgh
$ echo ${1: -7:0}

$ echo ${1: -7:2}
bc
$ echo ${1: -7:-2}
bcdef
$ array[0]=01234567890abcdefgh
$ echo ${array[0]:7}
7890abcdefgh
$ echo ${array[0]:7:0}

$ echo ${array[0]:7:2}
78
$ echo ${array[0]:7:-2}
7890abcdef
$ echo ${array[0]: -7}
bcdefgh
$ echo ${array[0]: -7:0}

$ echo ${array[0]: -7:2}
bc
$ echo ${array[0]: -7:-2}
bcdef

当parameter为@的情况,使用set -- ...设置positional parameter,$0的值自动生成:

$ 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:0}

$ echo ${@:7:2}
7 8
$ echo ${@:7:-2}
bash: -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
$ echo ${@: -7:0}

$

当parameter是数组的时候:

$ 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}

$

个人补充一点代码:

$ ABC=12345678
$ echo ${ABC::-1}  # default offset is 0
1234567
$ echo ${ABC:0:-1}
1234567
$ echo ${ABC: -1}  # a space between
8

大小写转换

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

This expansion modifies the case of alphabetic characters in parameter. The pattern is expanded to produce a pattern just as in filename expansion. Each character in the expanded value of parameter is tested against pattern, and, if it matches the pattern, its case is converted. The pattern should not attempt to match more than one character. The ‘^’ operator converts lowercase letters matching pattern to uppercase; the ‘,’ operator converts matching uppercase letters to lowercase. The ‘^^’ and ‘,,’ expansions convert each matched character in the expanded value; the ‘^’ and ‘,’ expansions match and convert only the first character in the expanded value. If pattern is omitted, it is treated like a ‘?’, which matches every character. If parameter is ‘@’ or ‘’, the case modification operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with ‘@’ or ‘’, the case modification operation is applied to each member of the array in turn, and the expansion is the resultant list.

$ A=abcdeABCDE
$ echo $A
abcdeABCDE
$ echo ${A,,}
abcdeabcde
$ echo ${A^^}
ABCDEABCDE
$ echo ${A,,[ACE]}  # convert all A|C|E to lower case
abcdeaBcDe
$ echo ${A^^[ace]}  # convert all a|c|e to upper case
AbCdEABCDE

将脚本所有输入参数全部统一转换成小写来处理:

$ set -- A B C D
$ set -- ${@,,}
$ echo $1 $2 $3 $4
a b c d

替换或删除匹配的子串

前删除

${parameter#word}  # shortest matching
${parameter##word} # longest matching

The word is expanded to produce a pattern and matched according to the rules described below (see Pattern Matching). If the pattern matches the beginning of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the # case) or the longest matching pattern (the ## case) deleted. If parameter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with @ or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

上文所述的Pattern Matching,就是bash的glob规则

$ echo $ABC
$ echo ${ABC#abc}
defg
$ echo ${ABC#abc*f}
g
abcdefg
$ echo ${ABC#*(c|b|a)}
abcdefg
$ echo ${ABC##*(c|b|a)}
defg

后删除

${parameter%word}  # shortest matching
${parameter%%word} # longest matching

The word is expanded to produce a pattern and matched according to the rules described below (see Pattern Matching). If the pattern matches a trailing portion of the expanded value of parameter, then the result of the expansion is the value of parameter with the shortest matching pattern (the % case) or the longest matching pattern (the %% case) deleted. If parameter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with @ or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

$ echo $ABC
abcdefg
$ echo ${ABC%efg}
abcd
$ echo ${ABC%b*efg}
a
$ echo ${ABC%*(e|f|g)}
abcdefg
$ echo ${ABC%%*(e|f|g)}
abcd

使用##和%实现basename和dirname功能

$ p='/usr/bin/abcde'
$ echo ${p##*/}  # basename
abcde
$ echo ${p%/*}   # dirname
/usr/bin

替换或删除

${parameter/pattern/string}  # longest matching

The pattern is expanded to produce a pattern just as in filename expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string. The match is performed according to the rules described below (see Pattern Matching). If pattern begins with /, all matches of pattern are replaced with string. Normally only the first match is replaced. If pattern begins with #, it must match at the beginning of the expanded value of parameter. If pattern begins with %, it must match at the end of the expanded value of parameter. If string is null, matches of pattern are deleted and the / following pattern may be omitted. If the nocasematch shell option (see the description of shopt in The Shopt Builtin) is enabled, the match is performed without regard to the case of alphabetic characters. If parameter is @ or *, the substitution operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with @ or *, the substitution operation is applied to each member of the array in turn, and the expansion is the resultant list.

$ echo $ABC
abcdefg1234567ijk
$ echo ${ABC/1*7/R}
abcdefgRijk
$ echo ${ABC/#abc/R}
Rdefg1234567ijk
$ echo ${ABC/%ijk/R}
abcdefg1234567R
$ echo ${ABC/1*7/}
abcdefgijk
$ echo ${ABC/#abc/}
defg1234567ijk
$ echo ${ABC/%ijk/}
abcdefg1234567

本文链接:https://cs.pynote.net/sf/linux/shell/202201252/

-- EOF --

-- MORE --