Bash中数组元素拼接字符串后处理异常的问题及解决咨询
Bash变量处理疑问及解决方法
问题背景
我对Bash变量类型还没完全理解,写了两段测试代码但结果不符合预期:
测试代码1
#!/bin/bash FILES=( "monitor_api*" ) FILE="./"${FILES[0]} echo $FILE TFILE="./monitor_api_0.3a.tar.gz" echo ${FILE:0:$(expr ${#FILE} - 3)} echo ${TFILE:0:$(expr ${#TFILE} - 3)}
执行结果1
./monitor_api_0.3a.tar.gz ./monitor_a ./monitor_api_0.3a.tar
测试代码2
#!/bin/bash FILES=( "monitor_api*" ) FILE=${FILES[0]} SFILE="./"$FILE echo $SFILE echo ${SFILE:0:-3} echo ${SFILE:0:(-3)} echo ${SFILE%.gz}
执行结果2
./monitor_api_0.3a.tar.gz ./monitor_a ./monitor_a ./monitor_api_0.3a.tar.gz
疑问
- 为何数组元素拼接的字符串与普通字符串表现不同?
- 如何让
$FILE成为可正常删除最后3字符的字符串? - 有没有更优的方法移除
$FILE变量中的.gz后缀?
解答
1. 差异原因
问题出在数组初始化的引号上:FILES=( "monitor_api*" ) 里的引号把*当成了字面字符,而非通配符去匹配文件。后续输出时虽然显示的是真实文件名,但变量实际存储的是./monitor_api*——只有echo输出时才触发通配符展开,变量本身的内容并没有被替换成真实文件名。
这导致用${#FILE}计算长度时,算的是./monitor_api*的长度(14个字符),而非真实文件名的长度。所以截取操作会从第11位截断,得到./monitor_a;而普通字符串TFILE直接存储完整文件名,长度计算正确,截断结果自然符合预期。
2. 让$FILE正常处理的方法
核心是让通配符*真正展开为真实文件名,去掉数组初始化的引号即可:
FILES=( monitor_api* )
此时Bash会自动匹配当前目录下所有以monitor_api开头的文件并存入数组,取第一个元素FILE=${FILES[0]}时,变量存储的就是真实文件名,后续字符串操作就能正常工作。
修改后的测试代码:
#!/bin/bash FILES=( monitor_api* ) FILE="./${FILES[0]}" echo $FILE echo ${FILE:0:-3} echo ${FILE%.gz}
执行结果:
./monitor_api_0.3a.tar.gz ./monitor_api_0.3a.tar ./monitor_api_0.3a.tar
3. 移除.gz后缀的更优方法
Bash内置了专门的后缀删除语法,比手动截取更可靠,无需计算长度:
${变量%.后缀}:从变量末尾删除最短匹配的后缀${变量%%.后缀}:从变量末尾删除最长匹配的后缀
针对.gz后缀,直接用${FILE%.gz}即可,前提是$FILE存储的是真实展开后的文件名。
如果要一步完成"获取第一个匹配文件+加./前缀+去.gz后缀",用数组方式更安全(避免文件名含特殊字符的问题):
FILES=( monitor_api* ) [[ -n ${FILES[0]} ]] && TARGET="./${FILES[0]%.gz}"
内容的提问来源于stack exchange,提问作者Nelson Teixeira
相关产品推荐
相关产品推荐

