以前没有使用bash正则表达式的答案这是一个纯bash解决方案,它将路径分为:
目录路径,其尾部/(如果存在)丢弃尾随/的正则表达式太长,以至于我没有发布它文件名,不包括(最后)点扩展名(最后一个)点扩展名及其前导。
该代码旨在处理所有可能的情况,欢迎您尝试。
#!/bin/bash
for path; do
####### the relevant part ######
[[ $path =~ ^(\.{1,2}|.*/\.{0,2})$|^(.*/)([^/]+)(\.[^/]*)$|^(.*/)(.+)$|^(.+)(\..*)$|^(.+)$ ]]
dirpath=${BASH_REMATCH[1]}${BASH_REMATCH[2]}${BASH_REMATCH[5]}
filename=${BASH_REMATCH[3]}${BASH_REMATCH[6]}${BASH_REMATCH[7]}${BASH_REMATCH[9]}
filext=${BASH_REMATCH[4]}${BASH_REMATCH[8]}
# dirpath should be non-null
[[ $dirpath ]] || dirpath='.'
################################
printf '%s=%q\n' \
path "$path" \
dirpath "$dirpath" \
filename "$filename" \
filext "$filext"
done
它是如何工作的?
基本上,它确保只有一个子表达式(在正则表达式中用|分隔)能够捕获输入。因此,您可以连接BASH_REMATCH中存储的所有相同类型的捕获组(例如,与目录路径相关的捕获组),因为最多有一个捕获组是非空的。
以下是一组扩展但并非详尽的示例的结果:
+--------------------------------------------------------+
| input dirpath filename filext |
+--------------------------------------------------------+
'' . '' ''
. . '' ''
.. .. '' ''
... . .. .
.file . .file ''
.file. . .file .
.file.. . .file. .
.file.Z . .file .Z
.file.sh.Z . .file.sh .Z
file . file ''
file. . file .
file.. . file. .
file.Z . file .Z
file.sh.Z . file.sh .Z
dir/ dir/ '' ''
dir/. dir/. '' ''
dir/... dir/ .. .
dir/.file dir/ .file ''
dir/.file. dir/ .file .
dir/.file.. dir/ .file. .
dir/.file.Z dir/ .file .Z
dir/.file.x.Z dir/ .file.x .Z
dir/file dir/ file ''
dir/file. dir/ file .
dir/file.. dir/ file. .
dir/file.Z dir/ file .Z
dir/file.x.Z dir/ file.x .Z
dir./. dir./. '' ''
dir./... dir./ .. .
dir./.file dir./ .file ''
dir./.file. dir./ .file .
dir./.file.. dir./ .file. .
dir./.file.Z dir./ .file .Z
dir./.file.sh.Z dir./ .file.sh .Z
dir./file dir./ file ''
dir./file. dir./ file .
dir./file.. dir./ file. .
dir./file.Z dir./ file .Z
dir./file.x.Z dir./ file.x .Z
dir// dir// '' ''
dir//. dir//. '' ''
dir//... dir// .. .
dir//.file dir// .file ''
dir//.file. dir// .file .
dir//.file.. dir// .file. .
dir//.file.Z dir// .file .Z
dir//.file.x.Z dir// .file.x .Z
dir//file dir// file ''
dir//file. dir// file .
dir//file.. dir// file. .
dir//file.Z dir// file .Z
dir//file.x.Z dir// file.x .Z
dir.//. dir.//. '' ''
dir.//... dir.// .. .
dir.//.file dir.// .file ''
dir.//.file. dir.// .file .
dir.//.file.. dir.// .file. .
dir.//.file.Z dir.// .file .Z
dir.//.file.x.Z dir.// .file.x .Z
dir.//file dir.// file ''
dir.//file. dir.// file .
dir.//file.. dir.// file. .
dir.//file.Z dir.// file .Z
dir.//file.x.Z dir.// file.x .Z
/ / '' ''
/. /. '' ''
/.. /.. '' ''
/... / .. .
/.file / .file ''
/.file. / .file .
/.file.. / .file. .
/.file.Z / .file .Z
/.file.sh.Z / .file.sh .Z
/file / file ''
/file. / file .
/file.. / file. .
/file.Z / file .Z
/file.sh.Z / file.sh .Z
/dir/ /dir/ '' ''
/dir/. /dir/. '' ''
/dir/... /dir/ .. .
/dir/.file /dir/ .file ''
/dir/.file. /dir/ .file .
/dir/.file.. /dir/ .file. .
/dir/.file.Z /dir/ .file .Z
/dir/.file.x.Z /dir/ .file.x .Z
/dir/file /dir/ file ''
/dir/file. /dir/ file .
/dir/file.. /dir/ file. .
/dir/file.Z /dir/ file .Z
/dir/file.x.Z /dir/ file.x .Z
/dir./. /dir./. '' ''
/dir./... /dir./ .. .
/dir./.file /dir./ .file ''
/dir./.file. /dir./ .file .
/dir./.file.. /dir./ .file. .
/dir./.file.Z /dir./ .file .Z
/dir./.file.sh.Z /dir./ .file.sh .Z
/dir./file /dir./ file ''
/dir./file. /dir./ file .
/dir./file.. /dir./ file. .
/dir./file.Z /dir./ file .Z
/dir./file.x.Z /dir./ file.x .Z
/dir// /dir// '' ''
/dir//. /dir//. '' ''
/dir//... /dir// .. .
/dir//.file /dir// .file ''
/dir//.file. /dir// .file .
/dir//.file.. /dir// .file. .
/dir//.file.Z /dir// .file .Z
/dir//.file.x.Z /dir// .file.x .Z
/dir//file /dir// file ''
/dir//file. /dir// file .
/dir//file.. /dir// file. .
/dir//file.Z /dir// file .Z
/dir//file.x.Z /dir// file.x .Z
/dir.//. /dir.//. '' ''
/dir.//... /dir.// .. .
/dir.//.file /dir.// .file ''
/dir.//.file. /dir.// .file .
/dir.//.file.. /dir.// .file. .
/dir.//.file.Z /dir.// .file .Z
/dir.//.file.x.Z /dir.// .file.x .Z
/dir.//file /dir.// file ''
/dir.//file. /dir.// file .
/dir.//file.. /dir.// file. .
/dir.//file.Z /dir.// file .Z
/dir.//file.x.Z /dir.// file.x .Z
// // '' ''
//. //. '' ''
//.. //.. '' ''
//... // .. .
//.file // .file ''
//.file. // .file .
//.file.. // .file. .
//.file.Z // .file .Z
//.file.sh.Z // .file.sh .Z
//file // file ''
//file. // file .
//file.. // file. .
//file.Z // file .Z
//file.sh.Z // file.sh .Z
//dir/ //dir/ '' ''
//dir/. //dir/. '' ''
//dir/... //dir/ .. .
//dir/.file //dir/ .file ''
//dir/.file. //dir/ .file .
//dir/.file.. //dir/ .file. .
//dir/.file.Z //dir/ .file .Z
//dir/.file.x.Z //dir/ .file.x .Z
//dir/file //dir/ file ''
//dir/file. //dir/ file .
//dir/file.. //dir/ file. .
//dir/file.Z //dir/ file .Z
//dir/file.x.Z //dir/ file.x .Z
//dir./. //dir./. '' ''
//dir./... //dir./ .. .
//dir./.file //dir./ .file ''
//dir./.file. //dir./ .file .
//dir./.file.. //dir./ .file. .
//dir./.file.Z //dir./ .file .Z
//dir./.file.sh.Z //dir./ .file.sh .Z
//dir./file //dir./ file ''
//dir./file. //dir./ file .
//dir./file.. //dir./ file. .
//dir./file.Z //dir./ file .Z
//dir./file.x.Z //dir./ file.x .Z
//dir// //dir// '' ''
//dir//. //dir//. '' ''
//dir//... //dir// .. .
//dir//.file //dir// .file ''
//dir//.file. //dir// .file .
//dir//.file.. //dir// .file. .
//dir//.file.Z //dir// .file .Z
//dir//.file.x.Z //dir// .file.x .Z
//dir//file //dir// file ''
//dir//file. //dir// file .
//dir//file.. //dir// file. .
//dir//file.Z //dir// file .Z
//dir//file.x.Z //dir// file.x .Z
//dir.//. //dir.//. '' ''
//dir.//... //dir.// .. .
//dir.//.file //dir.// .file ''
//dir.//.file. //dir.// .file .
//dir.//.file.. //dir.// .file. .
//dir.//.file.Z //dir.// .file .Z
//dir.//.file.x.Z //dir.// .file.x .Z
//dir.//file //dir.// file ''
//dir.//file. //dir.// file .
//dir.//file.. //dir.// file. .
//dir.//file.Z //dir.// file .Z
//dir.//file.x.Z //dir.// file.x .Z
如您所见,行为与basename和dirname不同。例如,basename dir/outputs dir,而正则表达式将为其提供一个空文件名。和这些被认为是目录,而不是文件名。
我用10000条256个字符的路径对其进行计时,耗时约1秒,而等效的POSIX外壳解决方案慢了2倍,基于通配符分叉(for循环内的外部调用)的解决方案慢至少60倍。
备注:不必测试包含\n或其他臭名昭著的字符的路径,因为所有字符都由bash的正则表达式引擎以相同的方式处理。唯一能够中断当前逻辑的字符是/和。,以目前意想不到的方式混合或倍增。当我第一次发布我的答案时,我发现了一些我必须解决的边界问题;我不能说正则表达式是100%防弹的,但它现在应该非常健壮。
顺便说一句,这里是POSIX shell解决方案,它产生了相同的输出:
#!/bin/sh
for path; do
####### the relevant part ######
fullname=${path##*/}
case $fullname in
. | ..)
dirpath="$path"
filename=''
filext=''
;;
*)
dirpath=${path%"$fullname"}
dirpath=${dirpath:-.} # dirpath should be non-null
filename=${fullname#.}
filename="${fullname%"$filename"}${filename%.*}"
filext=${fullname#"$filename"}
;;
esac
################################
printf '%s=%s\n' \
path "$path" \
dirpath "$dirpath" \
filename "$filename" \
filext "$filext"
done
附言:有些人可能不同意上述代码给出的结果,但有几点:
dotfile的特殊情况:原因是dotfile是一个UNIX概念。的特殊情况。和..:IMHO将它们视为目录似乎很明显,但大多数库不会这样做,并强制用户对结果进行后期处理。不支持双扩展名:这是因为您需要一个完整的数据库来存储所有有效的双扩展名,最重要的是,文件扩展名在UNIX中没有任何意义;例如,您可以调用tar存档mytaredfiles,这是完全正确的,您将能够毫无问题地tar xfmytaredfile。