Extract filename and extension in Bash

后端 未结 30 1939
野趣味
野趣味 2020-11-21 05:29

I want to get the filename (without extension) and the extension separately.

The best solution I found so far is:

NAME=`echo \"$FILE\" | cut -d\'.\'          


        
相关标签:
30条回答
  • 2020-11-21 06:19

    Based largely off of @mklement0's excellent, and chock-full of random, useful bashisms - as well as other answers to this / other questions / "that darn internet"... I wrapped it all up in a little, slightly more comprehensible, reusable function for my (or your) .bash_profile that takes care of what (I consider) should be a more robust version of 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
    }     
    

    Usage examples...

    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>
    
    0 讨论(0)
  • 2020-11-21 06:19

    A simple answer:

    To expand on the POSIX variables answer, note that you can do more interesting patterns. So for the case detailed here, you could simply do this:

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

    That will cut off the last occurrence of .tar.<something>.

    More generally, if you wanted to remove the last occurrence of .<something>.<something-else> then

    ${1.*.*}
    

    should work fine.

    The link the above answer appears to be dead. Here's a great explanation of a bunch of the string manipulation you can do directly in Bash, from TLDP.

    0 讨论(0)
  • 2020-11-21 06:20

    Mellen writes in a comment on a blog post:

    Using Bash, there’s also ${file%.*} to get the filename without the extension and ${file##*.} to get the extension alone. That is,

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

    Outputs:

    filename: thisfile
    extension: txt
    
    0 讨论(0)
  • 2020-11-21 06:20

    Magic file recognition

    In addition to the lot of good answers on this Stack Overflow question I would like to add:

    Under Linux and other unixen, there is a magic command named file, that do filetype detection by analysing some first bytes of file. This is a very old tool, initialy used for print servers (if not created for... I'm not sure about that).

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

    Standards extensions could be found in /etc/mime.types (on my Debian GNU/Linux desktop. See man file and man mime.types. Perhaps you have to install the file utility and mime-support packages):

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

    You could create a bash function for determining right extension. There is a little (not perfect) sample:

    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##*[/.-]}
    }
    

    This function could set a Bash variable that can be used later:

    (This is inspired from @Petesh right answer):

    filename=$(basename "$fullfile")
    filename="${filename%.*}"
    file2ext "$fullfile" extension
    
    echo "$fullfile -> $filename . $extension"
    
    0 讨论(0)
  • 2020-11-21 06:25

    Here are some alternative suggestions (mostly in awk), including some advanced use cases, like extracting version numbers for software packages.

    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 '^[/]\|^~/'
    

    All use cases are using the original full path as input, without depending on intermediate results.

    0 讨论(0)
  • 2020-11-21 06:27

    This is the only one that worked for me:

    path='folder/other_folder/file.js'
    
    base=${path##*/}
    echo ${base%.*}
    
    >> file
    

    This can also be used in string interpolation as well, but unfortunately you have to set base beforehand.

    0 讨论(0)
提交回复
热议问题