我想分别获取文件名(不带扩展名)和扩展名。

到目前为止,我找到的最佳解决方案是:

NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`

这是错误的,因为如果文件名包含多个,它就不起作用。字符。假设我有a.b.js,它会考虑a和b.js而不是a.b和js。

它可以在Python中用

file, ext = os.path.splitext(path)

但如果可能的话,我不希望仅仅为此启动Python解释器。

有更好的主意吗?


当前回答

以前没有使用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。

其他回答

从上面的答案来看,模仿Python的最短一行代码

file, ext = os.path.splitext(path)

假设您的文件确实有扩展名

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)

主要基于@mklement0的优秀,充斥着随机、有用的抨击——以及对这个/其他问题/“那该死的互联网”的其他答案。。。我用一个稍微更容易理解的、可重用的函数来概括我(或你)的.bash_profile,它考虑了(我认为)应该是一个更健壮的dirname/basename/what have you。。

function path { SAVEIFS=$IFS; IFS=""   # stash IFS for safe-keeping, etc.
    [[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return    # demand 2 arguments
    [[ $1 =~ ^(.*/)?(.+)?$ ]] && {     # regex parse the path
        dir=${BASH_REMATCH[1]}
        file=${BASH_REMATCH[2]}
        ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
        # edge cases for extensionless files and files like ".nesh_profile.coffee"
        [[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
        case "$2" in
             dir) echo      "${dir%/*}"; ;;
            name) echo      "${fnr%.*}"; ;;
        fullname) echo "${fnr%.*}.$ext"; ;;
             ext) echo           "$ext"; ;;
        esac
    }
    IFS=$SAVEIFS
}     

用法示例。。。

SOMEPATH=/path/to.some/.random\ file.gzip
path $SOMEPATH dir        # /path/to.some
path $SOMEPATH name       # .random file
path $SOMEPATH ext        # gzip
path $SOMEPATH fullname   # .random file.gzip                     
path gobbledygook         # usage: -bash <path> <dir|name|fullname|ext>
~% FILE="example.tar.gz"

~% echo "${FILE%%.*}"
example

~% echo "${FILE%.*}"
example.tar

~% echo "${FILE#*.}"
tar.gz

~% echo "${FILE##*.}"
gz

有关详细信息,请参阅Bash手册中的shell参数扩展。

您可以使用

sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-

获取文件名和

sed 's/^/./' | rev | cut -d. -f1  | rev

以获得扩展。

测试用例:

echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename"        | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename"        | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f1  | rev

Mellen在一篇博客文章中写道:

使用Bash,还有${file%.*}获取不带扩展名的文件名,${file##*.}单独获取扩展名。即,

file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"

输出:

filename: thisfile
extension: txt