views:

13441

answers:

7

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'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`

This is bad because it doesn't work if the filename contains multiple "." characters. If let's say I have a.b.js it will consider a and b.js, instead of a.b and js.

It can be easily done in Python with

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

but I'd prefer not to fire a Python interpreter just for this, if possible.

Any better ideas?

+4  A: 
~% FILE=prog.tar.gz
~% echo ${FILE%%.*}
prog
~% echo ${FILE%.*}
prog.tar
~% echo ${FILE#*.}
tar.gz
~% echo ${FILE##*.}
gz
Juliano
You (perhaps unintentionally) bring up the excellent question of what to do if the "extension" part of the filename has 2 dots in it, as in .tar.gz... I've never considered that issue, and I suspect it's not solvable without knowing all the possible valid file extensions up front.
rmeador
Why not solvable? In my example, it should be considered that the file contains *two* extensions, not an extension with two dots. You handle both extensions separately.
Juliano
It is unsolvable on a lexical basis, you'll need to check the file type. Consider if you had a game called `dinosaurs.in.tar` and you gzipped it to `dinosaurs.in.tar.gz` :)
Porges
+23  A: 

First, get file without path:

filename=$(basename $fullfile)
extension=${filename##*.}
filename=${filename%.*}
Petesh
Awesome! Thanks!
ionut bizau
Check out http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion for the full feature set.
D.Shawley
Add some quotes to "$fullfile", or you'll risk breaking the filename.
lhunath
Heck, you could even write filename="${fullfile##*/}" and avoid calling an extra `basename`
ephemient
+1  A: 
pax> echo a.b.js | sed 's/\.[^\.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js

works fine, so you can just use:

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

The commands, by the way, work as follows.

The NAME sed string substitutes a "." character followed by any number of non-"." characters up to the end of the line, with nothing (i.e., it removes everything from the final "." to the end of the line, inclusive). This is basically a non-greedy substitution using regex trickery.

The EXTENSION sed string substitutes a any number of characters followed by a "." character at the start of the line, with nothing (i.e., it removes everything from the start of the line to the final dot, inclusive). This is a greedy substitution which is the default action.

paxdiablo
A: 

This question explains this bash technique and several other related ones.

jjclarkson
A: 

That doesn't seem to work if the file has no extension, or no filename. Here is what I'm using; it only uses builtins and handles more (but not all) pathological filenames.

#!/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

And here are some testcases:

$ 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  = ""
Doctor J
A: 

Simply use ${parameter%word}

In your case:

${FILE%.*}

If you want to test it, all following work, and just remove the extension:

FILE = abc.xyz; echo ${FILE%.*};
FILE = 123.abc.xyz; echo ${FILE%.*};
FILE = abc; echo ${FILE%.*};
enyo
A: 

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. I.e.

file=”thisfile.txt”

echo “filename: ${file%.*}”

echo “extension: ${file##*.}”

outputs:

filename: thisfile

extension: txt

Kebabbert