代码之家  ›  专栏  ›  技术社区  ›  ibz

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

  •  1772
  • ibz  · 技术社区  · 17 年前

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

    迄今为止,我发现的最佳解决方案是:

    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解释器。

    有更好的主意吗?

    36 回复  |  直到 17 年前
        1
  •  3031
  •   Ludovic Kuty    7 年前

    首先,获取不带路径的文件名:

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

    或者,您可以将焦点放在路径的最后一个“/”,而不是“.”,即使您有不可预知的文件扩展名,它也可以工作:

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

    您可能需要检查文档:

        2
  •  545
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前
    ~% FILE="example.tar.gz"
    ~% echo "${FILE%%.*}"
    example
    ~% echo "${FILE%.*}"
    example.tar
    ~% echo "${FILE#*.}"
    tar.gz
    ~% echo "${FILE##*.}"
    gz
    

    有关详细信息,请参阅 shell parameter expansion 在bash手册中。

        3
  •  320
  •   Tomi Po    14 年前

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

    basename filename .extension
    

    例如:

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

    我们得到

    filename
    
        4
  •  130
  •   chepner    13 年前

    您可以使用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
    
        5
  •  67
  •   Doctor J    16 年前

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

    #!/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  = ""
    
        6
  •  42
  •   Bjarke Freund-Hansen najhi ullah    13 年前

    你可以使用 basename .

    例子:

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

    您需要为basename提供要删除的扩展名,但是如果您总是在执行 tar 具有 -z 那你就知道延期 .tar.gz .

    这应该是您想要的:

    tar -zxvf $1
    cd $(basename $1 .tar.gz)
    
        7
  •  28
  •   paxdiablo    8 年前
    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 替代A "." 字符后跟任意数量的非- “。” 到行尾的字符,不带任何内容(即,它从最后一行中删除所有内容 “。” 到行尾,包括行尾)。这基本上是一个使用regex欺骗的非贪婪替换。

    命令 EXTENSION 替换任意数量的字符,后跟 “。” 行开始处的字符,不带任何内容(即,它删除从行开始到最后一个点(包括在内)的所有内容)。这是一个贪婪的替换,这是默认操作。

        8
  •  26
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前

    梅伦在博客上发表评论:

    使用bash,还有 ${file%.*} 获取不带扩展名的文件名 ${file##*.} 单独获取扩展名。也就是说,

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

    输出:

    filename: thisfile
    extension: txt
    
        9
  •  24
  •   Some programmer dude    12 年前

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

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

    正如克莱顿休斯在一篇评论中所指出的,这对于问题中的实际例子是行不通的。所以作为一种选择,我建议使用 sed 对于扩展正则表达式,如下所示:

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

    它通过无条件地删除最后两个(字母-数字)扩展来工作。

    [安德斯·林达尔评论后再次更新]

        10
  •  22
  •   Cyker    9 年前

    不用麻烦了 awk sed 甚至 perl 对于这个简单的任务。有一次纯粹的狂欢, os.path.splitext() -只使用参数扩展的兼容解决方案。

    参考实施

    文件 os.path.splitext(path) :

    将路径名路径拆分为一对 (root, ext) 这样 root + ext == path 提取 为空或以句点开头,最多包含一个句点。忽略basename上的前导句点; 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"}"
    

    测验

    以下是 忽略前导句号 实现,它应该与每个输入上的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' |
    |---------------|-----------|-------|
    

    试验结果

    所有测试都通过了。

        11
  •  20
  •   henfiber    11 年前

    以下是一些备选建议(大部分在 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 '^[/]\|^~/'
    

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

        12
  •  16
  •   Let Me Tink About It    7 年前

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

    $ file=/blaabla/bla/blah/foo.txt
    echo $(basename ${file%.*}) # foo
    
        13
  •  13
  •   mklement0    7 年前

    这个 accepted answer 很好地工作 典型的 病例 但是 失败在 边缘 病例 ,即:

    • 对于没有扩展名的文件名(调用 后缀 在该答案的其余部分中, extension=${filename##*.} 返回输入文件名,而不是空字符串。
    • 扩展名=$文件名*. 不包括首字母 . 违反惯例。
      • 盲目的准备 . 不适用于没有后缀的文件名。
    • filename="${filename%.*}" 如果输入文件名以 . 不包含更多 . 字符(例如, .bash_profile )-违反惯例。

    ----------

    因此, 覆盖所有边缘情况的强大解决方案 呼唤一个 功能 -见下文定义; 可以返回 全部的 路径的组件 .

    示例调用:

    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
    

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

    • 没有后缀的文件名
    • 以开头的文件名 . ( 考虑后缀的开头)
    • 以…结尾的输入路径 / (拖尾) / 被忽略)
    • 仅为文件名的输入路径( . 作为父路径返回)
    • 文件名超过 . -带前缀的令牌(只有最后一个才被视为后缀):
    ----- /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
    
        14
  •  12
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前

    我认为,如果您只需要文件名,可以尝试以下操作:

    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。

        15
  •  10
  •   Sarfraaz Ahmed    11 年前

    可以强制剪切以显示所有字段和随后添加的字段 - 到字段编号。

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

    所以如果文件是 eth0.pcap.gz ,扩展名为 pcap.gz

    使用相同的逻辑,还可以使用“-”和CUT提取文件名,如下所示:

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

    这甚至适用于没有任何扩展名的文件名。

        16
  •  7
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前

    魔术文件识别

    除了这堆好答案溢出问题外,我还想补充:

    在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 file man mime.types . 也许你必须安装 文件 效用和 mime-support 包装):

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

    你可以创建一个 用于确定右扩展的函数。 有一些(不完美的)样本:

    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"
    
        17
  •  7
  •   Community Mohan Dere    9 年前

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

    这对我很有用:

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

    这会给你 stuff 作为文件名和 .tar.gz 作为扩展。它适用于任何数量的扩展,包括0。希望这对任何有同样问题的人都有帮助=)

        18
  •  6
  •   Joydip Datta    12 年前

    我用下面的脚本

    $ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
    foo
    
        19
  •  5
  •   Miriam English    15 年前
    $ F = "text file.test.txt"  
    $ echo ${F/*./}  
    txt  
    

    这可以处理文件名中的多个点和空格,但是如果没有扩展名,则返回文件名本身。但检查起来很容易;只需测试文件名和扩展名是否相同。

    当然,这个方法不适用于.tar.gz文件。然而,这可以通过两个步骤来处理。如果扩展名是gz,那么再次检查是否还有tar扩展名。

        20
  •  4
  •   Dennis    12 年前

    如何提取文件名和扩展名 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: 最后一个点上的拆分,对于包含点的文件名效果很好,但对于包含点的扩展名效果不佳。见下面的例子。

    用途:

    $ 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
    

    这确实 第一个例子就是警告,但是您必须处理每一个案例,这样根据您期望的扩展数量,它可能会更加繁琐。

        21
  •  4
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前

    这是代码 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
    
        22
  •  3
  •   SilverWolf danielbeard    8 年前

    简单使用 ${parameter%word}

    在你的情况下:

    ${FILE%.*}
    

    如果要测试它,请执行以下所有操作,只需删除扩展名:

    FILE=abc.xyz; echo ${FILE%.*};
    FILE=123.abc.xyz; echo ${FILE%.*};
    FILE=abc; echo ${FILE%.*};
    
        23
  •  2
  •   Alex Gray    12 年前

    基本上基于@mklement0的优秀,充满了随机性和实用性 巴什教 -以及其他问题的答案。我把它包装在一个小的,稍微更易于理解的,可重复使用的地方。 功能 为了我(或你的) .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>
    
        24
  •  2
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前

    一个简单的答案:

    扩展 POSIX variables answer ,请注意,您可以执行更有趣的模式。因此,对于这里详细介绍的案例,您可以简单地执行以下操作:

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

    这将切断最后一次出现的.tar。 <某物& .

    更一般地说,如果要删除的最后一个事件。 <某物& . <其他东西 然后

    ${1.*.*}
    

    应该工作得很好。

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

        25
  •  2
  •   Community Mohan Dere    9 年前

    建筑从 Petesh 如果只需要文件名, 路径和扩展都可以在一条线上剥离,

    filename=$(basename ${fullname%.*})
    
        26
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前

    从上面的答案来看,最短的一行程序可以模仿python的

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

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

    EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
    
        27
  •  1
  •   phil294    8 年前

    如果你还想允许 空的 扩展,这是我能想到的最短的:

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

    第一行解释说:它与path.ext或任何内容匹配,并将其替换为ext。如果匹配了任何内容,则不会捕获ext组。

        28
  •  1
  •   Bruno BEAUFILS    7 年前

    imho已经给出了最好的解决方案(使用shell参数扩展),目前是最好的解决方案。

    但是,我添加了一个只使用哑铃命令的命令,这是不高效的,任何严肃的人都不应该使用它:

    FILENAME=$(echo $FILE | cut -d . -f 1-$(printf $FILE | tr . '\n' | wc -l))
    EXTENSION=$(echo $FILE | tr . '\n' | tail -1)
    

    补充 只是为了好玩 -)

        29
  •  0
  •   Bill Gale    12 年前

    为了使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"
    
        30
  •  0
  •   Vytenis Bivainis    12 年前

    你可以使用

    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