字符串操作

    获取字符串长度的语法如下。

    下面是一个例子。

    1. $ echo ${#myPath}
    2. 29

    大括号{}是必需的,否则 Bash 会将$#理解成脚本的参数个数,将变量名理解成文本。

    1. $ echo $#myvar
    2. 0myvar

    上面例子中,Bash 将$#myvar分开解释了。

    字符串提取子串的语法如下。

    1. ${varname:offset:length}

    上面语法的含义是返回变量$varname的子字符串,从位置offset开始(从0开始计算),长度为length

    1. $ count=frogfootman
    2. $ echo ${count:4:4}
    3. foot

    上面例子返回字符串frogfootman从4号位置开始的长度为4的子字符串foot

    这种语法不能直接操作字符串,只能通过变量来读取字符串,并且不会改变原始字符串。变量前面的美元符号可以省略。

    1. # 报错
    2. $ echo ${"hello":2:3}

    上面例子中,"hello"不是变量名,导致 Bash 报错。

    如果省略length,则从位置offset开始,一直返回到字符串的结尾。

    1. $ count=frogfootman
    2. $ echo ${count:4}
    3. footman

    上面例子是返回变量count从4号位置一直到结尾的子字符串。

    如果offset为负值,表示从字符串的末尾开始算起。注意,负数前面必须有一个空格, 以防止与${variable:-word}的变量的设置默认值语法混淆。这时,如果还指定length,则length不能小于零。

    1. $ foo="This string is long."
    2. $ echo ${foo: -5}
    3. long.
    4. $ echo ${foo: -5:2}
    5. lo

    上面例子中,offset-5,表示从倒数第5个字符开始截取,所以返回long.。如果指定长度为2,则返回lo

    Bash 提供字符串搜索和替换的多种方法。

    (1)字符串头部的模式匹配。

    以下两种语法可以检查字符串开头,是否匹配给定的模式。如果匹配成功,就删除匹配的部分,返回剩下的部分。原始变量不会发生变化。

    1. # 如果 pattern 匹配变量 variable 的开头,
    2. # 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
    3. ${variable#pattern}
    4. # 如果 pattern 匹配变量 variable 的开头,
    5. # 删除最长匹配(贪婪匹配)的部分,返回剩余部分
    6. ${variable##pattern}

    匹配模式pattern可以使用*?[]等通配符。

    上面例子中,匹配的模式是/*/,其中*可以匹配任意数量的字符,所以最短匹配是,最长匹配是/home/cam/book/

    下面写法可以删除文件路径的目录部分,只留下文件名。

    1. $ path=/home/cam/book/long.file.name
    2. $ echo ${path##*/}
    3. long.file.name

    上面例子中,模式*/匹配目录部分,所以只返回文件名。

    下面再看一个例子。

    1. $ phone="555-456-1414"
    2. $ echo ${phone#*-}
    3. 456-1414
    4. 1414

    如果匹配不成功,则返回原始字符串。

    1. $ phone="555-456-1414"
    2. $ echo ${phone#444}
    3. 555-456-1414

    上面例子中,原始字符串里面无法匹配模式444,所以原样返回。

    如果要将头部匹配的部分,替换成其他内容,采用下面的写法。

    1. # 模式必须出现在字符串的开头
    2. ${variable/#pattern/string}
    3. # 示例
    4. $ foo=JPG.JPG
    5. $ echo ${foo/#JPG/jpg}
    6. jpg.JPG

    上面例子中,被替换的JPG必须出现在字符串头部,所以返回jpg.JPG

    (2)字符串尾部的模式匹配。

    以下两种语法可以检查字符串结尾,是否匹配给定的模式。如果匹配成功,就删除匹配的部分,返回剩下的部分。原始变量不会发生变化。

    1. # 如果 pattern 匹配变量 variable 的结尾,
    2. # 删除最短匹配(非贪婪匹配)的部分,返回剩余部分
    3. ${variable%pattern}
    4. # 如果 pattern 匹配变量 variable 的结尾,
    5. # 删除最长匹配(贪婪匹配)的部分,返回剩余部分
    6. ${variable%%pattern}

    上面两种语法会删除变量字符串结尾的匹配部分(将其替换为空),返回剩下的部分。区别是一个是最短匹配(又称非贪婪匹配),另一个是最长匹配(又称贪婪匹配)。

    1. $ path=/home/cam/book/long.file.name
    2. $ echo ${path%.*}
    3. /home/cam/book/long.file
    4. $ echo ${path%%.*}
    5. /home/cam/book/long

    上面例子中,匹配模式是.*,其中*可以匹配任意数量的字符,所以最短匹配是.name,最长匹配是.file.name

    下面写法可以删除路径的文件名部分,只留下目录部分。

    1. $ path=/home/cam/book/long.file.name
    2. $ echo ${path%/*}
    3. /home/cam/book

    上面例子中,模式/*匹配文件名部分,所以只返回目录部分。

    下面的写法可以替换文件的后缀名。

    1. $ file=foo.png
    2. $ echo ${file%.png}.jpg
    3. foo.jpg

    下面再看一个例子。

    如果匹配不成功,则返回原始字符串。

    如果要将尾部匹配的部分,替换成其他内容,采用下面的写法。

    1. # 模式必须出现在字符串的结尾
    2. ${variable/%pattern/string}
    3. # 示例
    4. $ foo=JPG.JPG
    5. JPG.jpg

    上面例子中,被替换的JPG必须出现在字符串尾部,所以返回JPG.jpg

    (3)任意位置的模式匹配。

    以下两种语法可以检查字符串内部,是否匹配给定的模式。如果匹配成功,就删除匹配的部分,换成其他的字符串返回。原始变量不会发生变化。

    1. # 如果 pattern 匹配变量 variable 的一部分,
    2. # 最长匹配(贪婪匹配)的那部分被 string 替换,但仅替换第一个匹配
    3. ${variable/pattern/string}
    4. # 如果 pattern 匹配变量 variable 的一部分,
    5. # 最长匹配(贪婪匹配)的那部分被 string 替换,所有匹配都替换
    6. ${variable//pattern/string}

    上面两种语法都是最长匹配(贪婪匹配)下的替换,区别是前一个语法仅仅替换第一个匹配,后一个语法替换所有匹配。

    1. $ path=/home/cam/foo/foo.name
    2. $ echo ${path/foo/bar}
    3. /home/cam/bar/foo.name
    4. $ echo ${path//foo/bar}
    5. /home/cam/bar/bar.name

    上面例子中,前一个命令只替换了第一个foo,后一个命令将两个foo都替换了。

    下面的例子将分隔符从:换成换行符。

    1. $ echo -e ${PATH//:/'\n'}
    2. /usr/local/bin
    3. /usr/bin
    4. /bin
    5. ...

    上面例子中,echo命令的-e参数,表示将替换后的字符串的\n字符,解释为换行符。

    模式部分可以使用通配符。

    1. $ phone="555-456-1414"
    2. $ echo ${phone/5?4/-}
    3. 55-56-1414

    上面的例子将5-4替换成-

    如果省略了string部分,那么就相当于匹配的部分替换成空字符串,即删除匹配的部分。

    1. $ path=/home/cam/foo/foo.name
    2. $ echo ${path/.*/}
    3. /home/cam/foo/foo

    上面例子中,第二个斜杠后面的string部分省略了,所以模式.*匹配的部分.name被删除后返回。

    前面提到过,这个语法还有两种扩展形式。

    1. # 模式必须出现在字符串的开头
    2. ${variable/#pattern/string}
    3. # 模式必须出现在字符串的结尾
    4. ${variable/%pattern/string}

    下面的语法可以改变变量的大小写。

    1. # 转为大写
    2. ${varname^^}
    3. # 转为小写
    4. ${varname,,}