9.2. 操作字符串
Bash已经支持了令人惊讶的字符串操作的数量。不幸地,这些工具缺乏统一的标准。一些是参数替换的子集,其它受到UNIX的expr命令的功能的影响。这导致不一致的命令语法和冗余的功能,但这些并没有引起混乱。
字符串长度
- ${#string}
- expr length $string
- expr "$string" : '.*'
1 stringZ=abcABC123ABCabc 2 3 echo ${#stringZ} # 15 4 echo `expr length $stringZ` # 15 5 echo `expr "$stringZ" : '.*'` # 15
例子 9-10. 在一个文本文件的段落之间插入一个空白行
1 #!/bin/bash 2 # paragraph-space.sh 3 4 # 给单倍行距的文本文件段落之间插入一个空白行. 5 # Usage: $0 <FILENAME 6 7 MINLEN=45 # 可能需要改变这个值. 8 # Assume lines shorter than $MINLEN characters 9 #+ terminate a paragraph. 10 11 while read line # 提供和输入文件一样多的行... 12 do 13 echo "$line" # 输出行本身. 14 15 len=${#line} 16 if [ "$len" -lt "$MINLEN" ] 17 then echo # 在一个短行结束后打印一个空白行. 18 fi 19 done 20 21 exit 0
匹配字符串开头的子串的长度
- expr match "$string" '$substring'
$substring是一个正则表达式.
expr "$string" : '$substring'
$substring是一个正则表达式.
1 stringZ=abcABC123ABCabc 2 # |------| 3 4 echo `expr match "$stringZ" 'abc[A-Z]*.2'` # 8 5 echo `expr "$stringZ" : 'abc[A-Z]*.2'` # 8
索引
- expr index $string $substring
在字符串$string中$substring第一次出现的数字位置
1 stringZ=abcABC123ABCabc 2 echo `expr index "$stringZ" C12` # 6 3 # C 字符的位置. 4 5 echo `expr index "$stringZ" 1c` # 3 6 # 'c' (in #3 position) matches before '1'.
这和C语言函数strchar()非常相似。
子串提取
- ${string:position}
把$string中从第$postion个字符开始字符串提取出来.
如果$string是"*"或"@",则表示从位置参数中提取第$postion后面的字符串。[1]
${string:position:length}
把$string中$postion个字符后面的长度为$length的字符串提取出来。
1 stringZ=abcABC123ABCabc 2 # 0123456789..... 3 # 以0开始计算. 4 5 echo ${stringZ:0} # abcABC123ABCabc 6 echo ${stringZ:1} # bcABC123ABCabc 7 echo ${stringZ:7} # 23ABCabc 8 9 echo ${stringZ:7:3} # 23A 10 # 提取的子串长为3 11 12 13 14 # 有没有可能从字符串的右边结尾处提取? 15 16 echo ${stringZ:-4} # abcABC123ABCabc 17 # 默认是整个字符串,就相当于${parameter:-default}. 18 # 然而. . . 19 20 echo ${stringZ:(-4)} # Cabc 21 echo ${stringZ: -4} # Cabc 22 # 这样,它可以工作了. 23 # 圆括号或附加的空白字符可以转义$position参数. 24 25 # 多谢Dan Jacobson指出这点.
如果$string参数是"*"或"@",则会提取第$length个位置参数开始的共$length个参数。[译者注:实际取得的参数有可能少于$length,因为有可能余下的参数没有那么多了]
1 echo ${*:2} # 打印第二个位置以后的参数. 2 echo ${@:2} # 和上面一样. 3 4 echo ${*:2:3} # 打印从第二个参数起的三个位置参数.
expr substr $string $position $length
提取$string中从位置$postition开始的长度为$length的子字符串。
1 stringZ=abcABC123ABCabc 2 # 123456789...... 3 # 以1开始计算. 4 5 echo `expr substr $stringZ 1 2` # ab 6 echo `expr substr $stringZ 4 3` # ABC
expr match "$string" '\($substring\)'
从$string字符串左边开始提取提取由$substring描述的正则表达式的子串。
expr "$string" : '\($substring\)'
从$string字符串左边开始提取由$substring描述的正则表达式的子串。
1 stringZ=abcABC123ABCabc 2 # ======= 3 4 echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1 5 echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1 6 echo `expr "$stringZ" : '\(.......\)'` # abcABC1 7 # 上面的每个echo都打印相同的结果.
expr match "$string" '.*\($substring\)'
从$string字符串结尾开始提取由$substring描述的正则表达式的子串。
expr "$string" : '.*\($substring\)'
从$string字符串结尾开始提取由$substring描述的正则表达式的子串。
1 stringZ=abcABC123ABCabc 2 # ====== 3 4 echo `expr match "$stringZ" '.*\([A-C][A-C][A-C][a-c]*\)'` # ABCabc 5 echo `expr "$stringZ" : '.*\(......\)'` # ABCabc
子串移动
- ${string#substring}
从$string左边开始,剥去最短匹配$substring子串.
${string##substring}
从$string左边开始,剥去最长匹配$substring子串.
1 stringZ=abcABC123ABCabc 2 # |----| 3 # |----------| 4 5 echo ${stringZ#a*C} # 123ABCabc 6 # 剥去匹配'a'到'C'之间最短的字符串. 7 8 echo ${stringZ##a*C} # abc 9 # 剥去匹配'a'到'C'之间最长的字符串.
${string%substring}
从$string结尾开始,剥去最短匹配$substring子串。
${string%%substring}
从$string结尾开始,剥去最长匹配$substring子串。
1 stringZ=abcABC123ABCabc 2 # || 3 # |------------| 4 5 echo ${stringZ%b*c} # abcABC123ABCa 6 # 从$stringZ后面尾部开始,剥去匹配'a'到'C'之间最短的字符串. 7 8 echo ${stringZ%%b*c} # a 9 # 从$stringZ后面尾部开始,剥去匹配'a'到'C'之间最长的字符串.
例子 9-11. 随着文件名的更改来转换图形文件的格式
1 #!/bin/bash 2 # cvt.sh: 3 # 把一个目录下的所有MacPaint图像文件转换成"pbm"格式. 4 5 # 使用软件包"netpbm"中的"macptopbm"程序来转换, 6 #+ 这个程序由Brian Henderson(bryanh@giraffe-data.com)维护. 7 # Netpbm是大多数Linux发行版的标准套件. 8 9 OPERATION=macptopbm 10 SUFFIX=pbm # 新的文件后缀. 11 12 if [ -n "$1" ] 13 then 14 directory=$1 # 如果一个目录名传递给脚本... 15 else 16 directory=$PWD # 否则使用当前目录. 17 fi 18 19 # 假定在目标目录中, 20 #+ 都是带着".mac"后缀的MacPaint图像文件. 21 22 for file in $directory/* # 文件名匹配符. 23 do 24 filename=${file%.*c} # 剥掉文件名中的".mac"后缀, 25 #+ '.*c'匹配所有'.'和'c'之间所有的匹配字符 26 27 $OPERATION $file > "$filename.$SUFFIX" 28 # 把结果重定向到新的文件中 29 rm -f $file # 转换后删除原来的文件. 30 echo "$filename.$SUFFIX" # 打印一条完成某文件的消息到标准输出. 31 done 32 33 exit 0 34 35 # 练习: 36 # -------- 37 # 依照现在的情况,这个脚本转换了目录下所有的文件 38 # 39 # 修改它,使它只转换后缀为".mac"的文件.
一个简单的使用子串提取结构的getopt模仿。
例子 9-12. 模仿getopt
1 #!/bin/bash 2 # getopt-simple.sh 3 # 作者: Chris Morgan 4 # 同意在ABS指南中使用. 5 6 7 getopt_simple() 8 { 9 echo "getopt_simple()" 10 echo "Parameters are '$*'" 11 until [ -z "$1" ] 12 do 13 echo "Processing parameter of: '$1'" 14 if [ ${1:0:1} = '/' ] 15 then 16 tmp=${1:1} # 剥去前导字符'/' . . . 17 parameter=${tmp%%=*} # 提取参数名. 18 value=${tmp##*=} # 提取参数值. 19 echo "Parameter: '$parameter', value: '$value'" 20 eval $parameter=$value 21 fi 22 shift 23 done 24 } 25 26 # 把所有选项传给函数getopt_simple(). 27 getopt_simple $* 28 29 echo "test is '$test'" 30 echo "test2 is '$test2'" 31 32 exit 0 33 34 --- 35 36 sh getopt_example.sh /test=value1 /test2=value2 37 38 Parameters are '/test=value1 /test2=value2' 39 Processing parameter of: '/test=value1' 40 Parameter: 'test', value: 'value1' 41 Processing parameter of: '/test2=value2' 42 Parameter: 'test2', value: 'value2' 43 test is 'value1' 44 test2 is 'value2'
子串替换
- ${string/substring/replacement}
用$replacement替换由$substring匹配的字符串。
${string//substring/replacement}
用$replacement替换所有匹配$substring的字符串。
1 stringZ=abcABC123ABCabc 2 3 echo ${stringZ/abc/xyz} # xyzABC123ABCabc 4 #用'xyz'代替第一个匹配的'abc'. 5 6 echo ${stringZ//abc/xyz} # xyzABC123ABCxyz 7 # 用'xyz'代替所有的'abc'.
${string/#substring/replacement}
如果$string字符串的最前端匹配$substring字符串,用$replacement替换$substring.
${string/%substring/replacement}
如果$string字符串的最后端匹配$substring字符串,用$replacement替换$substring.
1 stringZ=abcABC123ABCabc 2 3 echo ${stringZ/#abc/XYZ} # XYZABC123ABCabc 4 # 用'XYZ'替换前端的'abc'. 5 6 echo ${stringZ/%abc/XYZ} # abcABC123ABCXYZ 7 # 用'XYZ'替换后端的'abc'.
例子 9-13. 提取字符串的另一种办法
1 #!/bin/bash 2 # substring-extraction.sh 3 4 String=23skidoo1 5 # 012345678 Bash 6 # 123456789 awk 7 # 注意上面两个程序对索引的不同处理: 8 # Bash把字符串的第一个字符的标号称为'0'. 9 # Awk把字符串的第一个字符的标号称为'1'. 10 11 echo ${String:2:4} # position 3 (0-1-2), 4 characters long 12 # skid 13 14 # 在awk中与Bash的${string:pos:length}等同的是substr(string,pos,length). 15 echo | awk ' 16 { print substr("'"${String}"'",3,4) # skid 17 } 18 ' 19 # 用一个空的"echo"由管道传一个空的输入给awk, 20 #+ 这样就不必提供一个文件名给awk. 21 22 exit 0
9.2.2. 更深入的讨论
关于在脚本中字符串操作的更多细节,参考9.3 节和exp命令列表的相关章节。相关的脚本例子有:
注
这个要么用于命令行参数,要么用在函数的参数。.