Bash 字符串操作

Bash 字符串操作

Bash 中大部分的变量包括文件都可以当作字符串进行操作,因此本章的内容非常实用

字符串的长度

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

${#varname}

下面是一个例子。引号中的字符串也算长度。

$ name="xiashuo.xyz hello world"
$ echo ${#name}
23

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

$ echo $#myvar
0myvar

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

关于 $# 的分析,请看《Bash 脚本》

子字符串

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

${varname:offset:length}

上面语法的含义是返回变量 $varname 的子字符串,

首先我们需要明白一个下标定义,从左往右数,即正序的时候,第一个字符的下标是 0,从右往左数,即倒序的时候,第一个数的下标是 -1。

$ echo $name
0123456
$ echo ${name:2:3}
234
$ echo ${name:2:-1}
2345
$ echo ${name:2}
23456
$ echo ${name:9}

$ echo ${name: -4:2}
34
$ echo ${name: -4:-1}
345
$ echo ${name: -4:-5}
-bash: -5: substring expression < 0
$ echo ${name: -4}
3456
$ echo ${name: -9}

$ echo $name
0123456

我们可以通过一个变量来接受子字符串。获取子字符串的操作不会修改原字符串

$ name_sub=${name:3:2}
$ echo $name $name_sub
0123456 34

注意,不能直接对字符串字面量进行获取子字符串的操作,只能对值为字符串的变量执行获取子字符串的操作

$ echo ${"123456":3:2}
-bash: ${"123456":3:2}: bad substitution

字符串拼接

字符串的拼接很简单,直接写在一起即可

双引号字符串的拼接

# 字符串拼接字符串
str_concatnate="111""22"
echo $str_concatnate
# 字符串拼接变量
delimitor="+"
str_concatnate="111"$delimitor
echo $str_concatnate
str_concatnate="111"$delimitor"22"
echo $str_concatnate

输出

11122
111+
111+22

单引号字符串的拼接

# 字符串拼接字符串
str_concatnate='111''22'
echo $str_concatnate
# 字符串拼接变量
delimitor='+'
str_concatnate='111'$delimitor
echo $str_concatnate
str_concatnate='111'$delimitor'22'
echo $str_concatnate

输出

11122
111+
111+22

查找字符所在的位置

# 查找字符 c 或 8 的位置(哪个字母先出现就计算哪个):
# 输出 3
$ name=abcd123465789
$ echo "$(expr index "$name" c8)"

3

《Bash 的算术运算》的 expr 命令 小节

搜索和替换

Bash 提供多种模式的匹配和替换的方法

字符串头部的模式匹配

不存在语法 ${variable/##pattern/string}

${variable#pattern}${variable##pattern} 可以理解为 /string 中的 string 为空字符串。默认将匹配部分替换成空字符串,也就是删除

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

如果字符串里面无法匹配模式 pattern,则对字符串不会发生任何修改,最终返回原字符串。

简单实践一下:

$ str='xiashuo.xyz hello world!!!'
$ echo $str
xiashuo.xyz hello world!!!
$ echo ${str#*.}
xyz hello world!!!
# 最短匹配
$ echo ${str#*o}
.xyz hello world!!!
# 最长匹配
$ echo ${str##*o}
rld!!!
# 按最长匹配替换
$ echo ${str/#*o/111}
111rld!!!

常用用法,比如只获取文件名称(包括拓展名)、仅获取文件拓展类型、仅获取文件名(不包括拓展类型)

# 获取文件名+拓展名
$ echo ${file_path##*/}
Main.java
# 获取拓展名
$ echo ${file_path##*.}
java
# 仅获取文件名
$ file_name_full=${file_path##*/}
$ echo ${file_name_fullpattern}` 如果 pattern 匹配变量 variable 的结尾,删除最长匹配(贪婪匹配)的部分,返回剩余部分,原字符串不会有变化

- `${variable/%pattern/string}` 如果 pattern 匹配变量 variable 的结尾,就会将最长匹配(贪婪匹配)的部分替换成 string,原字符串不会有变化

> 不存在语法 `${variable/pattern}` 可以理解为 `/string` 中的 `string` 为空字符串。默认将匹配部分替换成空字符串,也就是删除

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

如果字符串里面无法匹配模式 `pattern`,则对字符串不会发生任何修改,最终返回原字符串。

简单实践:

```shell
$ str="xiashuo.xzy call my number\"158-4571-2256\""
$ echo $str
xiashuo.xzy call my number"158-4571-2256"
# 最短匹配
$ echo ${str%-*}
xiashuo.xzy call my number"158-4571
# 最长匹配
$ echo ${str%%-*}
xiashuo.xzy call my number"158
# 匹配引号,需要转义
$ echo ${str%%\"*\"}
xiashuo.xzy call my number
# 替换
$ echo ${str/%\"*\"/secret}
xiashuo.xzy call my numbersecret

常用用法,比如只获取文件名称(包括拓展名)、仅获取文件拓展类型、仅获取文件名(不包括拓展类型)

具体实践请看 字符串头部的模式匹配 小节

任意位置的模式匹配

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

# 模式可以包含  / 
$ echo ${fila_path////-}
-opt-shell-ts.sh
# 为了可读性,可以加上引号
$ echo ${fila_path/'/'/-}
-opt/shell/ts.sh
$ echo ${fila_path//'/'/-}
-opt-shell-ts.sh
# 省略第二个 / 后面的内容或者省略第二个 / 都是默认替换成空字符串,也就是删除 
$ echo ${fila_path//'/'/}
optshellts.sh
$ echo ${fila_path//'/'}
optshellts.sh

一个简单的应用是看 PATH 环境变量

: 替换成换行,然后可以一行一个路径,看着束缚很多

这个其实给了我们一种将单个字符串拆分成多行的思路,拆分成多行之后,再一行一行地读取到数组中。

$ echo $PATH
KAFKA_HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/lib/jvm/java/bin:/usr/lib/jvm/java/jre/bin:/root/bin
$ echo -e ${PATH//:/'\n'}
KAFKA_HOME/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/usr/lib/jvm/java/bin
/usr/lib/jvm/java/jre/bin
/root/bin

将单行字符串变成数组

关于数组的知识,请看《Bash 数组》

$ arr_str="111,2222,333"
$ arr=(${arr_str//,/' '})
$ echo ${#arr[@]}
3
$ echo ${arr[@]}
111 2222 333

此外还有两种特殊形式,我们在 字符串头部的模式匹配字符串尾部的模式匹配 中实际上就已经学习过了

# 模式必须出现在字符串的开头
${variable/#pattern/string}

# 模式必须出现在字符串的结尾
${variable/%pattern/string}

改变大小写

简单实践

$ tese_var=abcdefg
$ echo ${tese_var^^}
ABCDEFG
$ tese_var=ABCDEFG
$ echo ${tese_var,,}
abcdefg