一、字符串替换

  1. echo ${string/23/bb}   //abc1bb42341  替换一次    

  2. echo ${string//23/bb}  //abc1bb4bb41  双斜杠替换所有匹配    

  3. echo ${string/#abc/bb} //bb12342341   #以什么开头来匹配,根php中的^有点像    

  4. echo ${string/%41/bb}  //abc123423bb  %以什么结尾来匹配,根php中的$有点像   

  • sed命令

a\ 在当前行下面插入文本。
i\ 在当前行上面插入文本。
c\ 把选定的行改为新的文本。
d 删除,删除选择的行。
D 删除模板块的第一行。
s 替换指定字符
h 拷贝模板块的内容到内存中的缓冲区。
H 追加模板块的内容到内存中的缓冲区。
g 获得内存缓冲区的内容,并替代当前模板块中的文本。
G 获得内存缓冲区的内容,并追加到当前模板块文本的后面。
l 列表不能打印字符的清单。
n 读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。
N 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
p 打印模板块的行。
P(大写) 打印模板块的第一行。
q 退出Sed。
b lable 分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。
r file 从file中读行。
t label if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
T label 错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。
w file 写并追加模板块到file末尾。  
W file 写并追加模板块的第一行到file末尾。  
! 表示后面的命令对所有没有被选定的行发生作用。  
= 打印当前行号码。  
# 把注释扩展到下一个换行符以前。
  • sed替换标记

g 表示行内全面替换。  
p 表示打印行。  
w 表示把行写入一个文件。  
x 表示互换模板块中的文本和缓冲区中的文本。  
y 表示把一个字符翻译为另外的字符(但是不用于正则表达式)\1 子串匹配标记& 已匹配字符串标记

sed元字符集

^ 匹配行开始,如:/^sed/匹配所有以sed开头的行。
$ 匹配行结束,如:/sed$/匹配所有以sed结尾的行。
. 匹配一个非换行符的任意字符,如:/s.d/匹配s后接一个任意字符,最后是d。
* 匹配0个或多个字符,如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。
[] 匹配一个指定范围内的字符,如/[ss]ed/匹配sed和Sed。  
[^] 匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。
\(..\) 匹配子串,保存匹配的字符,如s/\(love\)able/\1rs,loveable被替换成lovers。
& 保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**。
\< 匹配单词的开始,如:/\<love/匹配包含以love开头的单词的行。
\> 匹配单词的结束,如/love\>/匹配包含以love结尾的单词的行。
x\{m\} 重复字符x,m次,如:/0\{5\}/匹配包含5个0的行。
x\{m,\} 重复字符x,至少m次,如:/0\{5,\}/匹配至少有5个0的行。
x\{m,n\} 重复字符x,至少m次,不多于n次,如:/0\{5,10\}/匹配5~10个0的行。

sed用法实例

替换操作:s命令

  • 替换文本中的字符串:

sed 's/book/books/' file
  • -n选项p命令一起使用表示只打印那些发生替换的行:

sed -n 's/test/TEST/p' file
  • 直接编辑文件选项-i,会匹配file文件中每一行的第一个book替换为books:

sed -i 's/book/books/g' file
  • 全面替换标记g

使用后缀 /g 标记会替换每一行中的所有匹配:

sed 's/book/books/g' file

二、字符串切割

1.使用 # 和 ## 来进行截取。

val='hello world!'
echo ${val#*o}     #结果:world!
echo ${val##*o}    #结果:rld!
  • 第一个是从左到右进行删除第一个o的字符串。 

  • 第二个是从左到右进行删除最后一个o的字符串。 

  • 这里的 * 表示的是通配符

2.使用%和%%进行截取 

第一个介绍了从左到右进行截取,下面我们就进行介绍一下关于从右到左进行截取。

val='hello world!'
echo ${val%o*}     #结果:hello w
echo ${val%%o*}    #结果:hell

这里的第一个说的就是从右向左进行截取到第一个o。 

第二个说的是从右向左截取到最后一个o。 

3.字符串长度

1 stringZ=abcABC123ABCabc
2 
3 echo ${#stringZ}                 # 15
4 echo `expr length $stringZ`      # 15
5 echo `expr "$stringZ" : '.*'`    # 15

4.在一个文本文件的段落之间插入空行

1 #!/bin/bash
2 # paragraph-space.sh
3 
4 # 在一个单倍行距的文本文件中插入空行.
5 # Usage: $0

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

  • 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'.

6.提取子串这与C语言中的strchr()函数非常相似.

  • ${string:position}

      在$string中从位置$position开始提取子串.

      如果$string是"*"或者"@", 那么将会提取从位置$position开始的位置参数[1]

  • ${string:position:length}

      在$string中从位置$position开始提取$length长度的子串.

stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

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
# 这样, 它就可以工作了.
# 使用圆括号或者添加一个空格可以"转义"这个位置参数.
  • 如果$string参数是"*"或"@", 那么将会从$position位置开始提取$length个位置参数, 但是由于可能没有$length个位置参数了, 那么就有几个位置参数就提取几个位置参数.

echo ${*:2}          # 打印出第2个和后边所有的位置参数.
echo ${@:2}          # 同上.
 
echo ${*:2:3}        # 从第2个开始, 连续打印3个位置参数.
  • 在$string中从$position开始提取$length长度的子串.expr substr $string $position $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
  • 从$string的开始位置提取$substring, $substring是正则表达式.expr match "$string" '\($substring\)'

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

从$string的开始位置提取$substring, $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都打印出相同的结果.
  • 从$string的结尾提取$substring, $substring是正则表达式.expr match "$string" '.*\($substring\)'

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

从$string的结尾提取$substring, $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

7.子串削除

  • ${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的结尾位置截掉'b'到'c'之间最短的匹配.

echo ${stringZ%%b*c}     # a
# 从$stringZ的结尾位置截掉'b'到'c'之间最长的匹配.

当你需要构造文件名的时候, 这个操作就显得特别有用.


9.练习

例子 9-1. 转换图片文件格式, 同时更改文件名

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 PERATION=macptopbm
10 SUFFIX=pbm          # 新的文件名后缀.
11 
12 if [ -n "$1" ]
13 then
14   directory=$1      # 如果目录名作为参数传递给脚本...
15 else
16   directory=$PWD    # 否则使用当前的工作目录.
17 fi  
18   
19 #  假定目标目录中的所有文件都是MacPaint格式的图像文件, 
20 #+ 并且都是以".mac"作为文件名后缀. 
21 
22 for file in $directory/*    # 文件名匹配(filename globbing).
23 do
24   filename=${file%.*c}      #  去掉文件名的".mac"后缀
25                             #+ ('.*c' 将会匹配
26        #+ '.'和'c'之间任意字符串).
27   $OPERATION $file > "$filename.$SUFFIX"
28                             # 把结果重定向到新的文件中.
29   rm -f $file               # 转换后删除原始文件.
30   echo "$filename.$SUFFIX"  # 从stdout输出转换后文件的文件名.
31 done
32 
33 exit 0
34 
35 # 练习:
36 # -----
37 #  就像它现在的样子, 这个脚本把当前
38 #+ 目录下的所有文件都转换了.
39 #  修改这个脚本, 让它只转换以".mac"为后缀名的文件.

例子 9-2. 将音频流文件转换为ogg各式的文件

1 #!/bin/bash
2 # ra2ogg.sh: 将音频流文件(*.ra)转换为ogg格式的文件.
3 
4 # 使用"mplayer"媒体播放器程序:
5 #      http://www.mplayerhq.hu/homepage
6 #      可能需要安装合适的编解码程序(codec)才能够正常的运行这个脚本. 
7 # 需要使用"ogg"库和"oggenc":
8 #      http://www.xiph.org/
9 
10 
11 FILEPREF=${1%%ra}      # 去掉"ra"后缀.
12 FILESUFF=wav           # wav文件的后缀.
13 UTFILE="$OFILEPREF""$OFILESUFF"
14 E_NOARGS=65
15 
16 if [ -z "$1" ]          # 必须要指定一个需要转换的文件名.
17 then
18   echo "Usage: `basename $0` [filename]"
19   exit $E_NOARGS
20 fi
21 
22 
23 ##########################################################################
24 mplayer "$1" -ao pcm:file=$OUTFILE
25 oggenc "$OUTFILE"  # oggenc编码后会自动加上正确的文件扩展名.
26 ##########################################################################
27 
28 rm "$OUTFILE"      # 删除中介的*.wav文件. 
29                    # 如果你想保留这个文件的话, 可以把上边这行注释掉.
30 
31 exit $?
32 
33 #  注意:
34 #  ----
35 #  在网站上, 简单的在*.ram流音频文件上单击的话, 
36 #+ 一般都只会下载真正音频流文件(就是*.ra文件)的URL.
37 #  你可以使用"wget"或者一些类似的工具
38 #+ 来下载*.ra文件本身.
39 
40 
41 #  练习:
42 #  -----
43 #  像上面所看到的, 这个脚本只能够转换*.ra文件.
44 #  给这个脚本添加一些灵活性, 让它能够转换*.ram and other filenames.
45 #
46 #  如果你觉得这还不过瘾, 那么你可以扩展这个脚本, 
47 #+ 让它自动下载并转换音频流文件.
48 #  给出一个URL, (使用"wget")批处理下载音频流文件,
49 #+ 然后转换它们.

一个简单的getopt命令的模拟, 使用子串提取结构.



例子 9-3. 模拟getopt

1 #!/bin/bash
2 # getopt-simple.sh
3 # 作者: Chris Morgan
4 # 已经经过授权, 可以使用在本书中.
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'.

如果$substring匹配$string的开头部分, 那么就用$replacement来替换$substring.${string/#substring/replacement}

${string/%substring/replacement}

如果$substring匹配$string的结尾部分, 那么就用$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.2.1. 使用awk来处理字符串

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


例子 9-14. 提取字符串的另一种方法

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} # 位置 3 (0-1-2), 4 个字符长
12                                          # skid
13 
14 # awk中等价于${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