首页 文章

在Bash中提取文件名和扩展名

提问于
浏览
1716

我想分别获取文件名(没有扩展名)和扩展名 .

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

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

这是错误的,因为如果文件名包含多个 . 字符,则它不起作用 . 如果,比方说,我有 a.b.js ,它会考虑 ab.js ,而不是 a.bjs .

它可以在Python中轻松完成

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

但是如果可能的话,我宁愿不为此启动Python解释器 .

还有更好的想法?

30 回答

  • 4

    你可以使用basename .

    例:

    $ basename foo-bar.tar.gz .tar.gz
    foo-bar
    

    您需要为basename提供应删除的扩展名,但是如果您始终使用 -z 执行 tar ,那么您知道扩展名为 .tar.gz .

    这应该做你想要的:

    tar -zxvf $1
    cd $(basename $1 .tar.gz)
    
  • 42

    [从单线程修改为通用bash函数,行为现在与 dirnamebasename 实用程序一致;理由补充说 . ]

    accepted answer works well in typical cases ,但 fails in edge cases ,即:

    • 对于没有扩展名的文件名(在本答案的其余部分中称为后缀), extension=${filename##*.} 返回输入文件名而不是空字符串 .
      与常规相反,

    • extension=${filename##*.} 不包括初始 . .

    • 盲目地预先设置 . 对于没有后缀的文件名不起作用 .

    • filename="${filename%.*}" 将是空字符串,如果输入文件名以 . 开头并且不包含其他 . 字符(例如, .bash_profile ) - 与惯例相反 .

    ---------

    因此, robust solution that covers all edge cases 的复杂性要求 function - 见下面的定义;它 can return all components of a path .

    示例电话:

    splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
    # -> $dir == '/etc'
    # -> $fname == 'bash.bashrc'
    # -> $fnameroot == 'bash'
    # -> $suffix == '.bashrc'
    

    请注意输入路径后面的参数是自由选择的位置变量名称 .
    要跳过那些之前不感兴趣的变量,请指定 _ (使用抛弃变量 $_ )或 '' ;例如,要仅提取文件名根目录和扩展名,请使用 splitPath '/etc/bash.bashrc' _ _ fnameroot extension .


    # SYNOPSIS
    #   splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]] 
    # DESCRIPTION
    #   Splits the specified input path into its components and returns them by assigning
    #   them to variables with the specified *names*.
    #   Specify '' or throw-away variable _ to skip earlier variables, if necessary.
    #   The filename suffix, if any, always starts with '.' - only the *last*
    #   '.'-prefixed token is reported as the suffix.
    #   As with `dirname`, varDirname will report '.' (current dir) for input paths
    #   that are mere filenames, and '/' for the root dir.
    #   As with `dirname` and `basename`, a trailing '/' in the input path is ignored.
    #   A '.' as the very first char. of a filename is NOT considered the beginning
    #   of a filename suffix.
    # EXAMPLE
    #   splitPath '/home/jdoe/readme.txt' parentpath fname fnameroot suffix
    #   echo "$parentpath" # -> '/home/jdoe'
    #   echo "$fname" # -> 'readme.txt'
    #   echo "$fnameroot" # -> 'readme'
    #   echo "$suffix" # -> '.txt'
    #   ---
    #   splitPath '/home/jdoe/readme.txt' _ _ fnameroot
    #   echo "$fnameroot" # -> 'readme'  
    splitPath() {
      local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
        # simple argument validation
      (( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
        # extract dirname (parent path) and basename (filename)
      _sp_dirname=$(dirname "$1")
      _sp_basename=$(basename "$1")
        # determine suffix, if any
      _sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '')
        # determine basename root (filemane w/o suffix)
      if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with '.'?
          _sp_basename_root=$_sp_basename
          _sp_suffix=''
      else # strip suffix from filename
        _sp_basename_root=${_sp_basename%$_sp_suffix}
      fi
      # assign to output vars.
      [[ -n $2 ]] && printf -v "$2" "$_sp_dirname"
      [[ -n $3 ]] && printf -v "$3" "$_sp_basename"
      [[ -n $4 ]] && printf -v "$4" "$_sp_basename_root"
      [[ -n $5 ]] && printf -v "$5" "$_sp_suffix"
      return 0
    }
    
    test_paths=(
      '/etc/bash.bashrc'
      '/usr/bin/grep'
      '/Users/jdoe/.bash_profile'
      '/Library/Application Support/'
      'readme.new.txt'
    )
    
    for p in "${test_paths[@]}"; do
      echo ----- "$p"
      parentpath= fname= fnameroot= suffix=
      splitPath "$p" parentpath fname fnameroot suffix
      for n in parentpath fname fnameroot suffix; do
        echo "$n=${!n}"
      done
    done
    

    执行该功能的测试代码:

    test_paths=(
      '/etc/bash.bashrc'
      '/usr/bin/grep'
      '/Users/jdoe/.bash_profile'
      '/Library/Application Support/'
      'readme.new.txt'
    )
    
    for p in "${test_paths[@]}"; do
      echo ----- "$p"
      parentpath= fname= fnameroot= suffix=
      splitPath "$p" parentpath fname fnameroot suffix
      for n in parentpath fname fnameroot suffix; do
        echo "$n=${!n}"
      done
    done
    

    预期输出 - 注意边缘情况:

    • 没有后缀的文件名

    • . 开头的文件名(不被视为后缀的开头)

    • / 结尾的输入路径(尾随 / 被忽略)

    • 仅作为文件名的输入路径( . 作为父路径返回)

    • 具有多个 . -prefixed标记的文件名(仅将最后一个标记为后缀):

    ----- /etc/bash.bashrc
    parentpath=/etc
    fname=bash.bashrc
    fnameroot=bash
    suffix=.bashrc
    ----- /usr/bin/grep
    parentpath=/usr/bin
    fname=grep
    fnameroot=grep
    suffix=
    ----- /Users/jdoe/.bash_profile
    parentpath=/Users/jdoe
    fname=.bash_profile
    fnameroot=.bash_profile
    suffix=
    ----- /Library/Application Support/
    parentpath=/Library
    fname=Application Support
    fnameroot=Application Support
    suffix=
    ----- readme.new.txt
    parentpath=.
    fname=readme.new.txt
    fnameroot=readme.new
    suffix=.txt
    
  • 6

    这是代码AWK . 它可以更简单地完成 . 但我对AWK并不擅长 .

    filename$ ls
    abc.a.txt  a.b.c.txt  pp-kk.txt
    filename$ find . -type f | awk -F/ '{print $2}' | rev | awk -F"." '{$1="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
    abc.a
    a.b.c
    pp-kk
    filename$ find . -type f | awk -F/ '{print $2}' | awk -F"." '{print $NF}'
    txt
    txt
    txt
    
  • 5

    好的,如果我理解正确,这里的问题是如何获得具有多个扩展名的文件的名称和完整扩展名,例如 stuff.tar.gz .

    这对我有用:

    fullfile="stuff.tar.gz"
    fileExt=${fullfile#*.}
    fileName=${fullfile%*.$fileExt}
    

    这将为 stuff 提供文件名, .tar.gz 为扩展名 . 它适用于任何数量的扩展,包括0.希望这对任何有相同问题的人都有帮助=)

  • 12
    pax> echo a.b.js | sed 's/\.[^.]*$//'
    a.b
    pax> echo a.b.js | sed 's/^.*\.//'
    js
    

    工作正常,所以你可以使用:

    pax> FILE=a.b.js
    pax> NAME=$(echo "$FILE" | sed 's/\.[^.]*$//')
    pax> EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
    pax> echo $NAME
    a.b
    pax> echo $EXTENSION
    js
    

    顺便说一句,这些命令的工作原理如下 .

    NAME 的命令替换 "." 字符,后跟任意数量的非 "." 字符,直到行的末尾,没有任何内容(即,它删除从最终 "." 到行尾的所有内容,包括在内) . 这基本上是使用正则表达式欺骗的非贪婪替换 .

    EXTENSION 的命令替换任意数量的字符,后跟行尾的 "." 字符,没有任何内容(即,它删除从行的开头到最后一个点的所有内容,包括在内) . 这是一个贪婪的替换,这是默认操作 .

  • 7

    一个简单的答案:

    要扩展POSIX variables answer,请注意您可以执行更多有趣的模式 . 因此,对于此处详述的案例,您可以简单地执行此操作:

    tar -zxvf $1
    cd ${1%.tar.*}
    

    这将切断.tar . <something>的最后一次出现 .

    更一般地说,如果你想删除最后一次出现 . <something> . <something-else>那么

    ${1.*.*}
    

    应该工作正常 .

    上述答案的链接似乎已经死了 . Here's a great explanation of a bunch of the string manipulation you can do directly in Bash, from TLDP .

  • 2

    我使用以下脚本

    $ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
    foo
    
  • 306

    梅伦在博客文章评论中写道:

    使用Bash,还有 ${file%.*} 来获取没有扩展名的文件名和 ${file##*.} 来单独获取扩展名 . 那是,

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

    输出:

    filename: thisfile
    extension: txt
    
  • 128

    魔术文件识别

    除了这个Stack Overflow问题的很多好答案之外,我想补充一下:

    在Linux和其他unixen下,有一个名为 file 的魔术命令,它通过分析文件的第一个字节来进行文件类型检测 . 这是一个非常古老的工具,最初用于打印服务器(如果没有创建...我不确定) .

    file myfile.txt
    myfile.txt: UTF-8 Unicode text
    
    file -b --mime-type myfile.txt
    text/plain
    

    标准扩展可以在 /etc/mime.types 中找到(在我的Debian GNU / Linux桌面上 . 请参阅 man fileman mime.types . 也许您必须安装 file 实用程序和 mime-support 包):

    grep $( file -b --mime-type myfile.txt ) </etc/mime.types
    text/plain      asc txt text pot brf srt
    

    您可以创建一个bash函数来确定正确的扩展名 . 有一点(不完美)的样本:

    file2ext() {
        local _mimetype=$(file -Lb --mime-type "$1") _line _basemimetype
        case ${_mimetype##*[/.-]} in
            gzip | bzip2 | xz | z )
                _mimetype=${_mimetype##*[/.-]}
                _mimetype=${_mimetype//ip}
                _basemimetype=$(file -zLb --mime-type "$1")
                ;;
            stream )
                _mimetype=($(file -Lb "$1"))
                [ "${_mimetype[1]}" = "compressed" ] &&
                    _basemimetype=$(file -b --mime-type - < <(
                            ${_mimetype,,} -d <"$1")) ||
                    _basemimetype=${_mimetype,,}
                _mimetype=${_mimetype,,}
                ;;
            executable )  _mimetype='' _basemimetype='' ;;
            dosexec )     _mimetype='' _basemimetype='exe' ;;
            shellscript ) _mimetype='' _basemimetype='sh' ;;
            * )
                _basemimetype=$_mimetype
                _mimetype=''
                ;;
        esac
        while read -a _line ;do
            if [ "$_line" == "$_basemimetype" ] ;then
                [ "$_line[1]" ] &&
                    _basemimetype=${_line[1]} ||
                    _basemimetype=${_basemimetype##*[/.-]}
                break
            fi
            done </etc/mime.types
        case ${_basemimetype##*[/.-]} in
            executable ) _basemimetype='' ;;
            shellscript ) _basemimetype='sh' ;;
            dosexec ) _basemimetype='exe' ;;
            * ) ;;
        esac
        [ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
          printf ${2+-v} $2 "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
          printf ${2+-v} $2 "%s" ${_basemimetype##*[/.-]}
    }
    

    此函数可以设置一个可以在以后使用的Bash变量:

    (这是受@Petesh正确答案的启发):

    filename=$(basename "$fullfile")
    filename="${filename%.*}"
    file2ext "$fullfile" extension
    
    echo "$fullfile -> $filename . $extension"
    
  • 20

    对于这个简单的任务,无需为 awksed 甚至 perl 而烦恼 . 有一个纯Bash, os.path.splitext() 兼容的解决方案,它只使用参数扩展 .

    参考实施

    os.path.splitext(path)的文档:

    将路径名路径拆分为一对(root,ext),使得root ext == path,ext为空或以句点开头,最多包含一个句点 . 基本名称的前导句点被忽略; splitext(' . cshrc')返回(' . cshrc','') .

    Python代码:

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

    Bash实施

    尊重领先期

    root="${path%.*}"
    ext="${path#"$root"}"
    

    忽略领先期

    root="${path#.}";root="${path%"$root"}${root%.*}"
    ext="${path#"$root"}"
    

    测试

    以下是Ignoring leading period实现的测试用例,它应该与每个输入上的Python参考实现相匹配 .

    |---------------|-----------|-------|
    |path           |root       |ext    |
    |---------------|-----------|-------|
    |' .txt'        |' '        |'.txt' |
    |' .txt.txt'    |' .txt'    |'.txt' |
    |' txt'         |' txt'     |''     |
    |'*.txt.txt'    |'*.txt'    |'.txt' |
    |'.cshrc'       |'.cshrc'   |''     |
    |'.txt'         |'.txt'     |''     |
    |'?.txt.txt'    |'?.txt'    |'.txt' |
    |'\n.txt.txt'   |'\n.txt'   |'.txt' |
    |'\t.txt.txt'   |'\t.txt'   |'.txt' |
    |'a b.txt.txt'  |'a b.txt'  |'.txt' |
    |'a*b.txt.txt'  |'a*b.txt'  |'.txt' |
    |'a?b.txt.txt'  |'a?b.txt'  |'.txt' |
    |'a\nb.txt.txt' |'a\nb.txt' |'.txt' |
    |'a\tb.txt.txt' |'a\tb.txt' |'.txt' |
    |'txt'          |'txt'      |''     |
    |'txt.pdf'      |'txt'      |'.pdf' |
    |'txt.tar.gz'   |'txt.tar'  |'.gz'  |
    |'txt.txt'      |'txt'      |'.txt' |
    |---------------|-----------|-------|
    

    测试结果

    所有测试都通过了

  • 0

    如何在fish中提取文件名和扩展名:

    function split-filename-extension --description "Prints the filename and extension"
      for file in $argv
        if test -f $file
          set --local extension (echo $file | awk -F. '{print $NF}')
          set --local filename (basename $file .$extension)
          echo "$filename $extension"
        else
          echo "$file is not a valid file"
        end
      end
    end
    

    Caveats: 最后一个点上的拆分,适用于带有点的文件名,但不适用于包含点的扩展名 . 见下面的例子 .

    Usage:

    $ split-filename-extension foo-0.4.2.zip bar.tar.gz
    foo-0.4.2 zip  # Looks good!
    bar.tar gz  # Careful, you probably want .tar.gz as the extension.
    

    可能有更好的方法来做到这一点 . 随意编辑我的答案以改进它 .


    如果有一组有限的扩展你将要处理,你知道所有这些扩展,试试这个:

    switch $file
      case *.tar
        echo (basename $file .tar) tar
      case *.tar.bz2
        echo (basename $file .tar.bz2) tar.bz2
      case *.tar.gz
        echo (basename $file .tar.gz) tar.gz
      # and so on
    end
    

    这确实是 not 作为第一个例子,但是您必须处理每个案例,因此根据您可以预期的扩展数量,它可能会更加繁琐 .

  • 27

    最小和最简单的解决方案(单线)是:

    $ file=/blaabla/bla/blah/foo.txt
    echo $(basename ${file%.*}) # foo
    
  • 14

    如果文件没有扩展名或没有文件名,那似乎不起作用 . 这是我正在使用的;它只使用内置函数并处理更多(但不是全部)病态文件名 .

    #!/bin/bash
    for fullpath in "$@"
    do
        filename="${fullpath##*/}"                      # Strip longest match of */ from start
        dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
        base="${filename%.[^.]*}"                       # Strip shortest match of . plus at least one non-dot char from end
        ext="${filename:${#base} + 1}"                  # Substring from len of base thru end
        if [[ -z "$base" && -n "$ext" ]]; then          # If we have an extension and no base, it's really the base
            base=".$ext"
            ext=""
        fi
    
        echo -e "$fullpath:\n\tdir  = \"$dir\"\n\tbase = \"$base\"\n\text  = \"$ext\""
    done
    

    以下是一些测试用例:

    $ basename-and-extension.sh / /home/me/ /home/me/file /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden /home/me/.hidden.tar /home/me/.. .
    /:
        dir  = "/"
        base = ""
        ext  = ""
    /home/me/:
        dir  = "/home/me/"
        base = ""
        ext  = ""
    /home/me/file:
        dir  = "/home/me/"
        base = "file"
        ext  = ""
    /home/me/file.tar:
        dir  = "/home/me/"
        base = "file"
        ext  = "tar"
    /home/me/file.tar.gz:
        dir  = "/home/me/"
        base = "file.tar"
        ext  = "gz"
    /home/me/.hidden:
        dir  = "/home/me/"
        base = ".hidden"
        ext  = ""
    /home/me/.hidden.tar:
        dir  = "/home/me/"
        base = ".hidden"
        ext  = "tar"
    /home/me/..:
        dir  = "/home/me/"
        base = ".."
        ext  = ""
    .:
        dir  = ""
        base = "."
        ext  = ""
    
  • 66

    您可以强制剪切以显示将 - 添加到字段编号的所有字段和后续字段 .

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

    所以如果FILE是 eth0.pcap.gz ,EXTENSION将是 pcap.gz

    使用相同的逻辑,您还可以使用带有cut的' - '来获取文件名,如下所示:

    NAME=`basename "$FILE" | cut -d'.' -f-1`
    

    这适用于没有任何文件名的文件名延期 .

  • 0

    通常您已经知道扩展名,因此您可能希望使用:

    basename filename .extension
    

    例如:

    basename /path/to/dir/filename.txt .txt
    

    我们得到了

    filename
    
  • 1

    以下是一些替代建议(主要在 awk 中),包括一些高级用例,例如提取软件包的版本号 .

    f='/path/to/complex/file.1.0.1.tar.gz'
    
    # Filename : 'file.1.0.x.tar.gz'
        echo "$f" | awk -F'/' '{print $NF}'
    
    # Extension (last): 'gz'
        echo "$f" | awk -F'[.]' '{print $NF}'
    
    # Extension (all) : '1.0.1.tar.gz'
        echo "$f" | awk '{sub(/[^.]*[.]/, "", $0)} 1'
    
    # Extension (last-2): 'tar.gz'
        echo "$f" | awk -F'[.]' '{print $(NF-1)"."$NF}'
    
    # Basename : 'file'
        echo "$f" | awk '{gsub(/.*[/]|[.].*/, "", $0)} 1'
    
    # Basename-extended : 'file.1.0.1.tar'
        echo "$f" | awk '{gsub(/.*[/]|[.]{1}[^.]+$/, "", $0)} 1'
    
    # Path : '/path/to/complex/'
        echo "$f" | awk '{match($0, /.*[/]/, a); print a[0]}'
        # or 
        echo "$f" | grep -Eo '.*[/]'
    
    # Folder (containing the file) : 'complex'
        echo "$f" | awk -F'/' '{$1=""; print $(NF-1)}'
    
    # Version : '1.0.1'
        # Defined as 'number.number' or 'number.number.number'
        echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?'
    
        # Version - major : '1'
        echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f1
    
        # Version - minor : '0'
        echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f2
    
        # Version - patch : '1'
        echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f3
    
    # All Components : "path to complex file 1 0 1 tar gz"
        echo "$f" | awk -F'[/.]' '{$1=""; print $0}'
    
    # Is absolute : True (exit-code : 0)
        # Return true if it is an absolute path (starting with '/' or '~/'
        echo "$f" | grep -q '^[/]\|^~/'
    

    所有用例都使用原始完整路径作为输入,而不依赖于中间结果 .

  • 10

    从上面的答案,最短的oneliner模仿Python的

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

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

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

    您可以使用cut命令删除最后两个扩展名( ".tar.gz" 部分):

    $ echo "foo.tar.gz" | cut -d'.' --complement -f2-
    foo
    

    正如Clayton Hughes在评论中指出的那样,这对问题中的实际例子不起作用 . 所以作为一种替代方案,我建议使用带有扩展正则表达式的 sed ,如下所示:

    $ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
    mpc-1.0.1
    

    它的工作原理是无条件地删除最后两个(字母数字)扩展 .

    [在Anders Lindahl发表评论后再次更新]

  • 23
    $ F = "text file.test.txt"  
    $ echo ${F/*./}  
    txt
    

    这适用于文件名中的多个点和空格,但是如果没有扩展名,则返回文件名本身 . 虽然容易检查;只测试文件名和扩展名是一样的 .

    当然,这种方法不适用于.tar.gz文件 . 但是,这可以通过两个步骤处理 . 如果扩展名为gz,则再次检查以查看是否还有tar扩展名 .

  • 2944

    为了使dir更有用(在没有路径的本地文件被指定为输入的情况下),我执行了以下操作:

    # Substring from 0 thru pos of filename
    dir="${fullpath:0:${#fullpath} - ${#filename}}"
    if [[ -z "$dir" ]]; then
        dir="./"
    fi
    

    这允许您执行一些有用的操作,例如为输入文件basename添加后缀:

    outfile=${dir}${base}_suffix.${ext}
    
    testcase: foo.bar
    dir: "./"
    base: "foo"
    ext: "bar"
    outfile: "./foo_suffix.bar"
    
    testcase: /home/me/foo.bar
    dir: "/home/me/"
    base: "foo"
    ext: "bar"
    outfile: "/home/me/foo_suffix.bar"
    
  • 1

    我想如果你只需要文件的名称,你可以试试这个:

    FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf
    
    # Remove all the prefix until the "/" character
    FILENAME=${FULLPATH##*/}
    
    # Remove all the prefix until the "." character
    FILEEXTENSION=${FILENAME##*.}
    
    # Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
    BASEDIRECTORY=${FULLPATH%$FILENAME}
    
    echo "path = $FULLPATH"
    echo "file name = $FILENAME"
    echo "file extension = $FILEEXTENSION"
    echo "base directory = $BASEDIRECTORY"
    

    这就是全部= D.

  • 527

    如果您还想允许 empty 扩展,这是我能想到的最短时间:

    echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION
    echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME
    

    第一行解释:它匹配PATH.EXT或ANYTHING并用EXT替换它 . 如果ANYTHING匹配,则不捕获ext组 .

  • 26

    你可以使用POSIX变量的神奇之处:

    bash-3.2$ FILENAME=somefile.tar.gz
    bash-3.2$ echo ${FILENAME%%.*}
    somefile
    bash-3.2$ echo ${FILENAME%.*}
    somefile.tar
    

    有一点需要注意,如果您的文件名是 ./somefile.tar.gz 形式,那么 echo ${FILENAME%%.*} 会贪婪地删除与 . 的最长匹配,并且您将拥有空字符串 .

    (您可以使用临时变量解决此问题:

    FULL_FILENAME=$FILENAME
    FILENAME=${FULL_FILENAME##*/}
    echo ${FILENAME%%.*}
    


    site解释得更多 .

    ${variable%pattern}
      Trim the shortest match from the end
    ${variable##pattern}
      Trim the longest match from the beginning
    ${variable%%pattern}
      Trim the longest match from the end
    ${variable#pattern}
      Trim the shortest match from the beginning
    
  • 12

    这是我用来查找文件的名称和扩展名的算法,当我编写一个Bash脚本,以便在名称与套管冲突时使名称唯一 .

    #! /bin/bash 
    
    #
    # Finds 
    # -- name and extension pairs
    # -- null extension when there isn't an extension.
    # -- Finds name of a hidden file without an extension
    # 
    
    declare -a fileNames=(
      '.Montreal' 
      '.Rome.txt' 
      'Loundon.txt' 
      'Paris' 
      'San Diego.txt'
      'San Francisco' 
      )
    
    echo "Script ${0} finding name and extension pairs."
    echo 
    
    for theFileName in "${fileNames[@]}"
    do
         echo "theFileName=${theFileName}"  
    
         # Get the proposed name by chopping off the extension
         name="${theFileName%.*}"
    
         # get extension.  Set to null when there isn't an extension
         # Thanks to mklement0 in a comment above.
         extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
    
         # a hidden file without extenson?
         if [ "${theFileName}" = "${extension}" ] ; then
             # hidden file without extension.  Fixup.
             name=${theFileName}
             extension=""
         fi
    
         echo "  name=${name}"
         echo "  extension=${extension}"
    done
    

    试运行 .

    $ config/Name\&Extension.bash 
    Script config/Name&Extension.bash finding name and extension pairs.
    
    theFileName=.Montreal
      name=.Montreal
      extension=
    theFileName=.Rome.txt
      name=.Rome
      extension=.txt
    theFileName=Loundon.txt
      name=Loundon
      extension=.txt
    theFileName=Paris
      name=Paris
      extension=
    theFileName=San Diego.txt
      name=San Diego
      extension=.txt
    theFileName=San Francisco
      name=San Francisco
      extension=
    $
    

    仅供参考:完整的音译程序和更多测试用例可在此处找到:https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0

  • 7

    首先,获取没有路径的文件名:

    filename=$(basename -- "$fullfile")
    extension="${filename##*.}"
    filename="${filename%.*}"
    

    或者,您可以专注于路径的最后一个'/'而不是' . '即使你有不可预测的文件扩展名,它应该工作:

    filename="${fullfile##*/}"
    

    您可能需要查看文档:

  • 4

    Petesh回答构建,如果只需要文件名,则可以在一行中删除路径和扩展名,

    filename=$(basename ${fullname%.*})
    
  • 2
    ~% FILE="example.tar.gz"
    ~% echo "${FILE%%.*}"
    example
    ~% echo "${FILE%.*}"
    example.tar
    ~% echo "${FILE#*.}"
    tar.gz
    ~% echo "${FILE##*.}"
    gz
    

    有关更多详细信息,请参阅Bash手册中的shell parameter expansion .

  • 1

    只需使用 ${parameter%word}

    在你的情况下:

    ${FILE%.*}
    

    如果您想测试它,以下所有工作,只需删除扩展名:

    FILE=abc.xyz; echo ${FILE%.*};
    FILE=123.abc.xyz; echo ${FILE%.*};
    FILE=abc; echo ${FILE%.*};
    
  • 0

    很大程度上取决于@ mklement0的优秀,充满了随机,有用的基本原理 - 以及对此/其他问题的其他答案/ "that darn internet" ...我把它全部包含在我的一点点,更易于理解,可重复使用的功能中(或者你的) .bash_profile 负责什么(我考虑)应该是 dirname / basename /你有什么更强大的版本..

    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 extesionless 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>
    
  • 2

    您可以使用

    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
    

相关问题