linux截取文件扩展名的原理

我在编写自己的Latex编译脚本la.sh的时候,取参数文件名的时候,这样处理:

1
2
3
4
5
6
7
8
# 取命令行参数
fullname=$1
# 取目录名
dirname=`dirname $fullname`
# 取文件名
filename=`basename $fullname`
# 取文件扩展名
extname=${fullname##*.}

其中最后一行取文件扩展名,当时是复制粘贴的,只知其然而不知其所以然。直到我找到这篇文章:

https://www.bookstack.cn/read/bash-tutorial/docs-string.md

摘取如下:

字符串头部的模式匹配

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

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

# 如果 pattern 匹配变量 variable 的开头
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分
max_str = ${variable##pattern}

字符串尾部的模式匹配

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

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

# 如果 pattern 匹配变量 variable 的结尾
# 删除最长匹配(贪婪匹配)的部分,返回剩余部分`
max_str=${variable%%pattern}

Summary

  1. # 从左到右匹配
  2. % 从右到左匹配
  3. 一个字符是最小匹配(懒惰匹配), 两个字符为最大匹配(贪婪匹配)
  4. pattern 可以包含 * ? [ ]

那么取主文件名就可以这样

1
firstname = ${filename%%.*}

我在不明原理的情况下还这么写了:

1
2
3
4
5
len=${#filename}
let "len=$len-4"
firstname=${filename:0:len}
dvifile="$firstname.dvi"
pdffile="$firstname.pdf"

绕个弯也可以啊。