Skip to content
🎨 作者:海针 - 搬运 📔 阅读量:

Shell字符串处理

1. 字母与ASCII码值的转换

1.1 ASCII

ASCII 是美国对于信息交换的标准代码,使用7位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符。 可以使用man ascii查看ASCII值列表,我们通常比较字符的大小,实际上是比对的ASCII码值。 截取如下:

OctDecHexCharOctDecHexChar
000000NUL '\0'1006440@
001101SOH (标题开始)1016541A
002202STX (本文开始)1026642B
003303ETX (本文结束)1036743C
004404EOT (传输结束)1046844D
005505ENQ (请求)1056945E
006606ACK (确认回应)1067046F
007707BEL '\a' (响铃)1077147G
010808BS '\b' (退格)1107248H
011909HT '\t' (水平定位符号)1117349I
012100ALF '\n' (换行键)112744AJ
013110BVT '\v' (垂直定位符号)113754BK
014120CFF '\f' (换页键)114764CL
015130DCR '\r' (Enter 键)115774DM
016140ESO (取消变换)116784EN
017150FSI (开始变换)117794FO
0201610DLE (跳出数据通讯)1208050P
0211711DC1 (设备控制1)1218151Q
0221812DC2 (设备控制2)1228252R
0231913DC3 (设备控制3)1238353S
0242014DC4 (设备控制4)1248454T
0252115NAK (确认失败回应)1258555U
0262216SYN (同步用暂停)1268656V
0272317ETB (区块传输结束)1278757W
0302418CAN (取消)1308858X
0312519EM (连接介质中断)1318959Y
032261ASUB (替换)132905AZ
033271BESC (退出键)133915B[
034281CFS (文件分区符)134925C\
035291DGS (群组分隔符)135935D]
036301ERS (记录分隔符)136945E^
037311FUS (单元分隔符)137955F_
0403220SPACE1409660`
0413321!1419761a
0423422"1429862b
0433523#1439963c
0443624$14410064d
0453725%14510165e
0463826&14610266f
047392714710367g
0504028(15010468h
0514129)15110569i
052422A*1521066Aj
053432B+1531076Bk
054442C,1541086Cl
055452D-1551096Dm
056462E.1561106En
057472F/1571116Fo
0604830016011270p
0614931116111371q
0625032216211472r
0635133316311573s
0645234416411674t
0655335516511775u
0665436616611876v
0675537716711977w
0705638817012078x
0715739917112179y
072583A:1721227Az
073593B;1731237B{
074603C<1741247C|
075613D=1751257D}
076623E>1761267E~
077633F?1771277FDEL

1.1 字母转换为ASCII码值

  • 方法一:printf
bash
test@test-PC:~/share/5$ printf "%d\n" "'a"       #注意'\n'是为了换行,让结果显示易读,也可以不要。
97
test@test-PC:~/share/5$ printf "%d\n" \'a
97
test@test-PC:~/share/5$ printf "%d\n" "'\\"      #打印‘\’的ascii这里需要转义
92
test@test-PC:~/share/5$ chr='?'
test@test-PC:~/share/5$ printf "%d\n" "'${chr}"   #可以使用变量
63
  • 方法二:od
bash
test@test-PC:~/share/5$ printf "A"| tr -d "\n" | od -An -t dC   #因为字符串中含有换行符,要去掉,否则也会打印换行符的ascii。
   65
test@test-PC:~/share/5$ echo -n "A"| od -An -t dC  #也可以这样不要换行符          
   65
test@test-PC:~/share/5$ printf 'A'| od -An -t dC   #这样也可以
   65

1.2 ASCII码值转换为字母

  • 方法一: awk
bash
test@test-PC:~/work/study/shell/share/5$ echo 65| awk '{printf("%c\n", $1)}'  
A
  • 方法二: printf
bash
test@test-PC:~/work/study/shell/share/5$ h=$(printf "%x" 65)  #先转换为16进制值,放入变量'h'
test@test-PC:~/work/study/shell/share/5$ printf "\\x$h\n"   #16进制输出为字符显示
A
test@test-PC:~/work/study/shell/share/5$ echo -e "\x$h"  #同上
A

也可以写在一行里:

bash
Atest@test-PC:~/share/5$ printf "\x$(printf '%x' 65)\n"  #注意,最后加\n只是为了输出可读性
A
test@test-PC:~/share/5$ printf "\\$(printf '%o' 65)\n"  
A
test@test-PC:~/share/5$

2. 进制转换

2.1 不同进制的数值赋值给变量

bash
test@test-PC:~/share/5$ ((num=2#1010)); echo $num    #以二进制赋值给变量 
10
test@test-PC:~/share/5$ ((num=8#11)); echo $num     #以八进制赋值给变量  
9
test@test-PC:~/share/5$ ((num=011)); echo $num    #以八进制赋值给变量  
9
test@test-PC:~/share/5$ ((num=16#FF)); echo $num  #以十六进制赋值给变量  
255
test@test-PC:~/share/5$ ((num=0XFF)); echo $num   #以十六进制赋值给变量  
255

当然也可以多此一举,以十进制值同赋值给变量

bash
test@test-PC:~/share/5$ ((num=10#25)); echo $num  
25
test@test-PC:~/share/5$ ((num=25)); echo $num   
25

2.2 不同进制转换为十进制输出

  • 方法一: $(( expressions )) 表达式 表示方法$(( BASE#NUMBER )), BASE代表进制,可为2,8,10,16进制,NUMBER以对应进制的形式显示。
    注意:这里NUMBER不需要再带进制前缀,因为BASE已经指明了,如0xFF转换为十进制,只需要$((16#FF))而不能使用$((16#0xFF))
bash
test@test-PC:~/share/5$ num2="1010"; echo $(( 2#$num2 ))  #二进制转十进制
10
test@test-PC:~/share/5$ num8="11"; echo $(( 8#$num8 ))    #八进制转十进制
9
test@test-PC:~/share/5$ num16="FF"; echo $(( 16#$num16 ))  #十六进制转十进制
255

如果十六进制字符串前缀已经带了0X或者0x,需要先删除这两个字符。

bash
test@test-PC:~/share/5$ num16="0XFF"; echo $(( 16#${num16:2} ))  #十六进制转十进制
255

或者

bash
test@test-PC:~/share/5$ let num16=0xFF; echo $num16
255
test@test-PC:~share/5$ num=0xff; echo $(($num))   #默认是以十进制输出
255
  • 方法二: bcbc进制转换需要2个内置变量,ibaseobaseibase为输入进制,obase为输出进制,默认为十进制输出。
bash
test@test-PC:~/share/5$ num2='1010';echo "ibase=2;$num2" | bc  #二转十
10
test@test-PC:~/share/5$ num8='11';echo "ibase=8;$num8" | bc    #八转十
9
test@test-PC:~/share/5$ num16='FF';echo "ibase=16;$num16" | bc #十六转十
255

2.3 十进制转换为不同进制输出

  • 方法一: printf
bash
test@test-PC:~/share/5$ num=9; printf "%o\n" $num    #输出为八进制
11
test@test-PC:~/share/5$ num=255; printf "%x\n" $num  #输出为十六进制
ff
test@test-PC:~/share/5$ num=255; printf "%X\n" $num  #大写的十六进制
FF
test@test-PC:~/share/5$ num=255; printf "0X%X\n" $num  #加0X前缀十六进制
0XFF

printf不支持直接输出十进制数据,我们可以通过其它方法实现,比如用bc

  • 方法二: bc
bash
test@test-PC:~/share/5$ num=10;echo "obase=2; $num" | bc    
1010
test@test-PC:~/share/5$ num=9; echo "obase=8; $num" | bc
11
test@test-PC:~/share/5$ num=255; echo "obase=16; $num" | bc  
FF

3. 字符串与数组的转换

3.1 数组转换为字符串

在Bash中,你可以使用"${array[@]}""${array[*]}"来获取数组的所有元素,并使用printfecho结合字符串连接符""将它们转换为单个字符串。

shell
#!/bin/bash

# 声明一个数组
array=(one two three)

# 使用 printf 将数组转换为以空格分隔的字符串
string=$(printf "%s " "${array[@]}")
echo "Array as string (with spaces): $string"

# 使用 printf 将数组转换为以特定字符分隔的字符串
delimiter=,
string=$(printf "%s$delimiter" "${array[@]}")
# 移除末尾的分隔符
string=${string%,}
echo "Array as string (with commas): $string"

3.2 字符串转换为数组

要将字符串转换为数组,你可以使用内置的read命令或者直接通过赋值操作。

shell
#!/bin/bash

# 声明一个以空格分隔的字符串
string="one two three"

# 使用内置的 read 命令将字符串转换为数组
read -ra array <<< "$string"
echo "String converted to array:"
for item in "${array[@]}"; do
  echo "$item"
done

# 或者直接赋值
array=($string)
echo "String converted to array using direct assignment:"
for item in "${array[@]}"; do
  echo "$item"
done

# 如果字符串是以逗号分隔的,可以这样做
string="one,two,three"
IFS=',' read -ra array <<< "$string"
echo "Comma-separated string converted to array:"
for item in "${array[@]}"; do
  echo "$item"
done

在上面的代码中,IFS代表内部字段分隔符(Internal Field Separator),它定义了如何分割字符串。通过设置IFS=',',我们告诉read命令使用逗号作为字段分隔符。在使用完IFS之后,最好将其重置为默认值,以避免影响脚本中其他部分的执行。

4. Bash字符串处理

声明一个字符串,并赋初值,本节对字符串的处理都是这个串作为示例。
string="https://zhidao.baidu.com"

4.1 字符串变量处理

表达式说明
${string}变量string的值, 与$string相同
${string-DEFAULT}如果string没有被声明, 那么就以$DEFAULT作为其值
${string:-DEFAULT}如果string没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值,判断var变量是否没有定义
${string=DEFAULT}如果string没有被声明, 那么就以$DEFAULT作为其值
${string:=DEFAULT}如果string没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 ,判断var变量是否没有定义,并确保变量始终有值
${string+OTHER}如果string声明了, 那么其值就是$OTHER, 否则就为null字符串
${string:+OTHER}如果string被设置了, 那么其值就是$OTHER, 否则就为null字符串
${string?ERR_MSG}如果string没被声明, 那么就打印$ERR_MSG
${string:?ERR_MSG}如果string没被设置, 那么就打印$ERR_MSG
${!varprefix*}匹配之前所有以varprefix开头进行声明的变量
${!varprefix@}匹配之前所有以varprefix开头进行声明的变量

4.2 字符串长度

表达式说明
${#string}计算字符串string长度。
bash
test@test-PC:~/share/5$ echo ${#string}
24

4.3 字符串截取

表达式说明
${string#substring}从变量$string的左侧开始, 删除最短匹配$substring的子串。
${string##substring}从变量$string的左侧开始, 删除最长匹配$substring的子串。
${string%substring}从变量$string的右侧开始, 删除最短匹配$substring的子串。
${string%%substring}从变量$string的右侧开始, 删除最长匹配$substring的子串。
bash
test@test-PC:~/share/5$ echo ${string#*.}  #从左开始,删除匹配到第1个.及其前面的子串
baidu.com
test@test-PC:~/share/5$ echo ${string##*.} #从左开始,删除匹配到最后1个.及其前面的子串
com
test@test-PC:~/share/5$ echo ${string%.*}  #从右开始,删除匹配到第1个.及其后面的子串
https://zhidao.baidu
test@test-PC:~/share/5$ echo ${string%%.*} #从右开始,删除匹配到最后1个.及其后面的子串
https://zhidao

4.4 字符串替换

表达式说明
${string/substring/replacement}使用$replacement, 来代替第一个匹配的$substring的子串。
${string//substring/replacement}使用$replacement, 代替所有匹配的$substring
${string/#substring/replacement}从变量string的右侧开始, 删除最短匹配substring的子串。
${string/%substring/replacement}从变量string的右侧开始, 删除最长匹配substring的子串。

4.5 字符串连接

将多个字符串并排放到一起就能实现字符中的连接 。

bash
test@test-PC:~/work/study$ A='aaa'; echo "Output:$A"
Output:aaa
test@test-PC:~/work/study$ A='aaa';B='bbb'; echo $A$B
aaabbb
test@test-PC:~/work/study$ C=$A$B;echo $C
aaabbb

4.6 字符串切片操作

通过 ${变量名:起始:长度} 得到子字符串。

表达式说明
${string:offset}返回字符串变量string中从第offset个字符后面\(不包括第offset个字符\)的字符开始,到最后的部分。
注意:offset 的取值在0${#var}-1 之间。
${string:offset:number}返回字符串变量string中从第offset个字符后面\(不包括第offset个字符\)的字符开始,长度为number的部分。
${string: -length}取字符串的最右侧几个字符。
注意:冒号后必须有一空白字符。
${string:offset: -length}从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容。
${string: -length:offset}先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容。
注意:-length前空格。
bash
test@test-PC:~/work/study$ echo ${url:8}
www.debian.org
test@test-PC:~/work/study$ echo ${url:0:1}
h
test@test-PC:~/work/study$ echo ${url:1:3}
ttp
test@test-PC:~/work/study$ echo ${url:8: -4}
www.debian
test@test-PC:~/work/study$ echo ${url: -3:3}
org

4.7 字符串比较大小

字符串是按照从左向右对字母的ASCII值大小进行比较,不关心字符串的长度,比较过程中左侧同等位置的字母大者字符串为大。

表达式说明
= 等于,如:if [ "$a" = "$b" ]
==等于,如:if [ "$a" == "$b" ],=等价
注意:==的功能在[[]][]中的行为是不同的,如下:
1 [[ $a == z* ]] # 如果$a"z"开头(模式匹配)那么将为true
2 [[ $a == "z*" ]] # 如果$a等于z*(字符匹配),那么结果为true
3 [ $a == z* ] # 如果$a等于z*(字符匹配),那么结果为true
4 [ "$a" == "z*" ] # 如果$a等于z*(字符匹配),那么结果为true
!=不等于,如:if [ "$a" != "$b" ], 这个操作符将在[[]]结构中使用模式匹配.
<小于,在ASCII字母顺序下.如:
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ][]结构中"<"需要被转义.
>大于,在ASCII字母顺序下.如:
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ][]结构中">"需要被转义.
-z字符串为空.就是长度为0.
-n字符串不为空