Extract filename and extension in Bash
Extract filename and extension in Bash
Question
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 wrong because it doesn't work if the file name 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 up a Python interpreter just for this, if possible.
Any better ideas?
Accepted Answer
First, get file name without the path:
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
Alternatively, you can focus on the last '/' of the path instead of the '.' which should work even if you have unpredictable file extensions:
filename="${fullfile##*/}"
You may want to check the documentation :
- On the web at section "3.5.3 Shell Parameter Expansion"
- In the bash manpage at section called "Parameter Expansion"
Read more… Read less…
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz
For more details, see shell parameter expansion in the Bash manual.
Usually you already know the extension, so you might wish to use:
basename filename .extension
for example:
basename /path/to/dir/filename.txt .txt
and we get
filename
You can use the magic of POSIX parameter expansion:
bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo "${FILENAME%%.*}"
somefile
bash-3.2$ echo "${FILENAME%.*}"
somefile.tar
There's a caveat in that if your filename was of the form ./somefile.tar.gz
then echo ${FILENAME%%.*}
would greedily remove the longest match to the .
and you'd have the empty string.
(You can work around that with a temporary variable:
FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}
)
This site explains more.
${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
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 "[email protected]"
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 = ""
You can use basename
.
Example:
$ basename foo-bar.tar.gz .tar.gz
foo-bar
You do need to provide basename with the extension that shall be removed, however if you are always executing tar
with -z
then you know the extension will be .tar.gz
.
This should do what you want:
tar -zxvf $1
cd $(basename $1 .tar.gz)