shell教程
OutOfMemory.CN技术专栏-> shell-> shell教程-> 9.2. 操作字符串

9.2. 操作字符串

Bash已经支持了令人惊讶的字符串操作的数量。不幸地,这些工具缺乏统一的标准。一些是参数替换的子集,其它受到UNIX的expr命令的功能的影响。这导

Bash已经支持了令人惊讶的字符串操作的数量。不幸地,这些工具缺乏统一的标准。一些是参数替换的子集,其它受到UNIX的expr命令的功能的影响。这导致不一致的命令语法和冗余的功能,但这些并没有引起混乱。

字符串长度

${#string}

expr length $string

expr "$string" : '.*'

stringZ=abcABC123ABCabc

echo ${#stringZ}                 # 15
echo `expr length $stringZ`      # 15
echo `expr "$stringZ" : '.*'`    # 15


例子 9-10. 在一个文本文件的段落之间插入一个空白行

#!/bin/bash
# paragraph-space.sh

# 给单倍行距的文本文件段落之间插入一个空白行.
# Usage: $0 <FILENAME

MINLEN=45        # 可能需要改变这个值.
#  Assume lines shorter than $MINLEN characters
#+ terminate a paragraph.

while read line  # 提供和输入文件一样多的行...
do
  echo "$line"   # 输出行本身.

  len=${#line}
  if [ "$len" -lt "$MINLEN" ]
    then echo    # 在一个短行结束后打印一个空白行.
  fi
done

exit 0

匹配字符串开头的子串的长度

expr match "$string" '$substring'

$substring 是一个正则表达式.

expr "$string" : '$substring'

$substring 是一个正则表达式.

stringZ=abcABC123ABCabc
#       |------|

echo `expr match "$stringZ" 'abc[A-Z]*.2'`   # 8
echo `expr "$stringZ" : 'abc[A-Z]*.2'`       # 8

索引

expr index $string $substring

在字符串$string中$substring第一次出现的数字位置

stringZ=abcABC123ABCabc
echo `expr index "$stringZ" C12`             # 6
                                             # C 字符的位置.

echo `expr index "$stringZ" 1c`              # 3
# 'c' (in #3 position) matches before '1'.

这和C语言函数strchar()非常相似。

子串提取
${string:position}

把$string中从第$postion个字符开始字符串提取出来.

如果$string是"*"或"@",则表示从位置参数中提取第$postion后面的字符串。[1]

${string:position:length}

把$string中$postion个字符后面的长度为$length的字符串提取出来。

stringZ=abcABC123ABCabc
#       0123456789.....
#       以0开始计算.

echo ${stringZ:0}                            # abcABC123ABCabc
echo ${stringZ:1}                            # bcABC123ABCabc
echo ${stringZ:7}                            # 23ABCabc

echo ${stringZ:7:3}                          # 23A
                                             # 提取的子串长为3



# 有没有可能从字符串的右边结尾处提取?

echo ${stringZ:-4}                           # abcABC123ABCabc
# 默认是整个字符串,就相当于${parameter:-default}.
# 然而. . .

echo ${stringZ:(-4)}                         # Cabc
echo ${stringZ: -4}                          # Cabc
# 这样,它可以工作了.
# 圆括号或附加的空白字符可以转义$position参数.

# 多谢Dan Jacobson指出这点.

如果$string参数是"*"或"@",则会提取第$length个位置参数开始的共$length个参数。[译者注:实际取得的参数有可能少于$length,因为有可能余下的参数没有那么多了]

echo ${*:2}          # 打印第二个位置以后的参数.
echo ${@:2}          # 和上面一样.

echo ${*:2:3}        # 打印从第二个参数起的三个位置参数.

expr substr $string $position $length

提取$string中从位置$postition开始的长度为$length的子字符串。

stringZ=abcABC123ABCabc
#       123456789......
#       以1开始计算.

echo `expr substr $stringZ 1 2`              # ab
echo `expr substr $stringZ 4 3`              # ABC

expr match "$string" '\($substring\)'

从$string字符串左边开始提取提取由$substring描述的正则表达式的子串。

expr "$string" : '\($substring\)'

从$string字符串左边开始提取由$substring描述的正则表达式的子串。

stringZ=abcABC123ABCabc
#       =======

echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'`   # abcABC1
echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'`       # abcABC1
echo `expr "$stringZ" : '\(.......\)'`                   # abcABC1
# 上面的每个echo都打印相同的结果.

expr match "$string" '.*\($substring\)'

从$string字符串结尾开始提取由$substring描述的正则表达式的子串。

expr "$string" : '.*\($substring\)'

从$string字符串结尾开始提取由$substring描述的正则表达式的子串。

stringZ=abcABC123ABCabc
#                ======

echo `expr match "$stringZ" '.*\([A-C][A-C][A-C][a-c]*\)'`    # ABCabc
echo `expr "$stringZ" : '.*\(......\)'`                       # ABCabc

子串移动

${string#substring}

从$string左边开始,剥去最短匹配$substring子串.

${string##substring}

从$string左边开始,剥去最长匹配$substring子串.

stringZ=abcABC123ABCabc
#       |----|
#       |----------|

echo ${stringZ#a*C}      # 123ABCabc
# 剥去匹配'a'到'C'之间最短的字符串.

echo ${stringZ##a*C}     # abc
# 剥去匹配'a'到'C'之间最长的字符串.

${string%substring}

从$string结尾开始,剥去最短匹配$substring子串。

${string%%substring}

从$string结尾开始,剥去最长匹配$substring子串。

stringZ=abcABC123ABCabc
#                    ||
#        |------------|

echo ${stringZ%b*c}      # abcABC123ABCa
# 从$stringZ后面尾部开始,剥去匹配'a'到'C'之间最短的字符串.

echo ${stringZ%%b*c}     # a
# 从$stringZ后面尾部开始,剥去匹配'a'到'C'之间最长的字符串.


例子 9-11. 随着文件名的更改来转换图形文件的格式

#!/bin/bash
#  cvt.sh:
#  把一个目录下的所有MacPaint图像文件转换成"pbm"格式.

#  使用软件包"netpbm"中的"macptopbm"程序来转换,
#+ 这个程序由Brian Henderson(bryanh@giraffe-data.com)维护.
#  Netpbm是大多数Linux发行版的标准套件.

OPERATION=macptopbm
SUFFIX=pbm          # 新的文件后缀.

if [ -n "$1" ]
then
  directory=$1      # 如果一个目录名传递给脚本...
else
  directory=$PWD    # 否则使用当前目录.
fi

#  假定在目标目录中,
#+ 都是带着".mac"后缀的MacPaint图像文件.

for file in $directory/*    # 文件名匹配符.
do
  filename=${file%.*c}      #  剥掉文件名中的".mac"后缀,
                            #+ '.*c'匹配所有'.'和'c'之间所有的匹配字符

  $OPERATION $file > "$filename.$SUFFIX"
                            # 把结果重定向到新的文件中
  rm -f $file               # 转换后删除原来的文件.
  echo "$filename.$SUFFIX"  # 打印一条完成某文件的消息到标准输出.
done

exit 0

# 练习:
# --------
#  依照现在的情况,这个脚本转换了目录下所有的文件
#
#  修改它,使它只转换后缀为".mac"的文件.

一个简单的使用子串提取结构的getopt模仿。


例子 9-12. 模仿getopt

#!/bin/bash
# getopt-simple.sh
# 作者: Chris Morgan
# 同意在ABS指南中使用.


getopt_simple()
{
    echo "getopt_simple()"
    echo "Parameters are '$*'"
    until [ -z "$1" ]
    do
      echo "Processing parameter of: '$1'"
      if [ ${1:0:1} = '/' ]
      then
          tmp=${1:1}               # 剥去前导字符'/' . . .
          parameter=${tmp%%=*}     # 提取参数名.
          value=${tmp##*=}         # 提取参数值.
          echo "Parameter: '$parameter', value: '$value'"
          eval $parameter=$value
      fi
      shift
    done
}

# 把所有选项传给函数getopt_simple().
getopt_simple $*

echo "test is '$test'"
echo "test2 is '$test2'"

exit 0

---

sh getopt_example.sh /test=value1 /test2=value2

Parameters are '/test=value1 /test2=value2'
Processing parameter of: '/test=value1'
Parameter: 'test', value: 'value1'
Processing parameter of: '/test2=value2'
Parameter: 'test2', value: 'value2'
test is 'value1'
test2 is 'value2'

子串替换

${string/substring/replacement}

用$replacement替换由$substring匹配的字符串。

${string//substring/replacement}

用$replacement替换所有匹配$substring的字符串。

stringZ=abcABC123ABCabc

echo ${stringZ/abc/xyz}           # xyzABC123ABCabc
                                  #用'xyz'代替第一个匹配的'abc'.

echo ${stringZ//abc/xyz}          # xyzABC123ABCxyz
                                  # 用'xyz'代替所有的'abc'.

${string/#substring/replacement}

如果$string字符串的最前端匹配$substring字符串,用$replacement替换$substring.

${string/%substring/replacement}

如果$string字符串的最后端匹配$substring字符串,用$replacement替换$substring.

stringZ=abcABC123ABCabc

echo ${stringZ/#abc/XYZ}          # XYZABC123ABCabc
                                  # 用'XYZ'替换前端的'abc'.

echo ${stringZ/%abc/XYZ}          # abcABC123ABCXYZ
                                  # 用'XYZ'替换后端的'abc'.

9.2.1. 用awk处理字符串

Bash脚本可以调用awk的字符串操作功能来代替它自己内建的字符串操作符.


例子 9-13. 提取字符串的另一种办法

#!/bin/bash
# substring-extraction.sh

String=23skidoo1
#      012345678    Bash
#      123456789    awk
# 注意上面两个程序对索引的不同处理:
# Bash把字符串的第一个字符的标号称为'0'.
# Awk把字符串的第一个字符的标号称为'1'.

echo ${String:2:4} # position 3 (0-1-2), 4 characters long
                                         # skid

# 在awk中与Bash的${string:pos:length}等同的是substr(string,pos,length).
echo | awk '
{ print substr("'"${String}"'",3,4)      # skid
}
'
#  用一个空的"echo"由管道传一个空的输入给awk,
#+ 这样就不必提供一个文件名给awk.

exit 0

9.2.2. 更深入的讨论

关于在脚本中字符串操作的更多细节,参考9.3 节exp命令列表的相关章节。相关的脚本例子有:

  1. 例子 12-9
  2. 例子 9-16
  3. 例子 9-17
  4. 例子 9-18
  5. 例子 9-20

[1]

这个要么用于命令行参数,要么用在函数的参数。.

© 内存溢出 OutOfMemory.CN