How can I remove the extension of a filename in a shell script?
Solution 1
You should be using the command substitution syntax $(command)
when you want to execute a command in script/command.
So your line would be
name=$(echo "$filename" | cut -f 1 -d '.')
Code explanation:
-
echo
get the value of the variable$filename
and send it to standard output - We then grab the output and pipe it to the
cut
command - The
cut
will use the . as delimiter (also known as separator) for cutting the string into segments and by-f
we select which segment we want to have in output - Then the
$()
command substitution will get the output and return its value - The returned value will be assigned to the variable named
name
Note that this gives the portion of the variable up to the first period .
:
$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello
Solution 2
You can also use parameter expansion:
$ filename=foo.txt
$ echo "${filename%.*}"
foo
Just be aware that if there is no file extension, it will look further back for dots, e.g.
- If the filename only starts with a dot (e.g.
.bashrc
) it will remove the whole filename. - If there's a dot only in the path (e.g.
path.to/myfile
or./myfile
), then it will trim inside the path.
Solution 3
If you know the extension, you can use basename
$ basename /home/jsmith/base.wiki .wiki
base
Solution 4
If your filename contains a dot (other than the one of the extension) then use this:
echo $filename | rev | cut -f 2- -d '.' | rev
Solution 5
file1=/tmp/main.one.two.sh
t=$(basename "$file1") # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//') # output is main.one.two
use whichever you want. Here I assume that last .
(dot) followed by text is extension.
Related videos on Youtube
mimicocotopus
Updated on February 15, 2022Comments
-
mimicocotopus about 2 years
What's wrong with the following code?
name='$filename | cut -f1 -d'.''
As is, I get the literal string
$filename | cut -f1 -d'.'
, but if I remove the quotes I don't get anything. Meanwhile, typing"test.exe" | cut -f1 -d'.'
in a shell gives me the output I want,
test
. I already know$filename
has been assigned the right value. What I want to do is assign to a variable the filename without the extension.-
mpe over 11 years
basename $filename .exe
would do the same thing. That's assuming you always know what extension you want to remove. -
Charles Duffy almost 8 years@mpe, you mean
basename "$filename" .exe
. Otherwise filenames with spaces would be bad news.
-
-
mimicocotopus over 11 yearsThanks. I also noticed that I need to use the echo command. name=
echo $filename | cut -f1 -d'.'
-
jordanm over 11 yearsBackticks are deprecated by POSIX,
$()
is preferred. -
Jens over 8 yearsForking and piping to get at a few characters is about the worst solution imaginable.
-
Scott Stensland about 8 yearsThe problem with this answer is it assumes input string has ONLY one dot ... @chepner below has a much better solution ... name=${filename%.*}
-
Carlos Robles almost 8 yearshere the explanation of the command: gnu.org/software/bash/manual/html_node/…
-
user208145 over 7 yearsAnd here I was about to use
echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev
. Thanks. -
supreme Pooba over 7 yearsI forgot the middle rev, but once I saw it, this was great!
-
neric about 7 yearsThis answer is characteristic of beginners and should not be spreaded. Use the builtin mechanism as described by chepner's answer
-
Hawker65 about 6 yearsDoes this work with files with multiple extensions like
image.png.gz
? -
chepner about 6 years
%.*
will only remove the last extension; if you want to remove all the extensions, use%%.*
. -
gniourf_gniourf about 6 yearsHow is this different from the answer given by Steven Penny 3 years ago?
-
Dale C. Anderson over 5 yearsThis seems to work way better than the accepted answer. It only shaves off the last extension if the file has more than one dot, whereas cut removes everything after the first dot.
-
Frotz over 5 yearsThis one strips "tar.gz" by selecting characters before the first instance of a dot in the filename not counting the path. One probably doesn't want to strip extensions that way.
-
Gabriel Staples almost 5 yearsHow can I remove just
.wiki
and end up with/home/jsmith/base
? -
Gabriel Staples almost 5 yearsUpdate: answer: See stackoverflow.com/a/32584935/4561887. Works perfectly!
-
jpmc26 over 4 yearsBe warned this deletes the entire name if there is no extension and the path is relative. E.g.,
filename=./ihavenoextension
. -
jpmc26 over 4 yearsFails on
./ihavenoextension
, just like the other answer. -
Y00 over 4 yearsbe really careful if you want to use more than one dot like ./text.log
-
nassim about 4 yearsThanks I was looking for some time for this. I used it to trim the last octet of an IP address echo 192.168.5.123 | cut -f -3 -d '.'
-
Hibou57 about 4 yearsIt’s even better with an
-s
option given tocut
, so that it returns an empty string whenever the filename contains no dot. -
Tim Krief almost 4 yearsThis should be the accepted answer in my opinion, since it works on path with dots in them, with hidden files starting with a dot, or even with file with multiple extensions.
-
FedKad over 3 yearsWhat happens when
file1=/tmp.d/mainonetwosh
? The sed expression should be replaced with's/\.[^./]*$//'
-
FedKad over 3 yearsWhat happens with a file like this:
file=/tmp.d/foo
? -
FedKad over 3 yearsWhat happens with a file like this:
filename=/tmp.d/foo
? -
Manish Singh over 3 years@FedonKadifeli, this works for just the file name, not for full path. But you can get the filename first using a similar approach by using
/
as the delimiter. -
FedKad over 3 yearsIt has problems with "dot files" also. Try with
.profile
for example. -
mwfearnley over 3 years@Hawker65 if you know the file will have a
.png.gz
extension, you can use{file%.png.gz}
. That will prevent it trimming after periods earlier in the file - or in the path! -
mwfearnley over 3 yearsIf you know it ends in
.tar.gz
, you can just use$(basename "$a" .tar.gz)
. Also, make sure to wrap your variable in quotes everywhere if there's any chance it will contain spaces or other weird characters! -
mwfearnley over 3 years@gniourf_gniourf upvoting because you've made it a useful answer by showing how to correctly use it with a variable.
-
Jinyu over 3 yearsit doesn't work if filename is "a.b.c.txt'
-
shaheen g about 3 yearsThis covers all the possibilities: stackoverflow.com/a/66177670/12969125
-
shaheen g about 3 yearsThis covers all the possibilities: stackoverflow.com/a/66177670/12969125
-
donvercety about 3 years@FedonKadifeli use something like this:
basename "$INPUT" | rev | cut -f 2- -d '.' | rev
works perfectly.$INPUT
is the filename or the path. -
virtualdj over 2 yearsThanks, the only answer that works with
./ihavenoextension
! -
Nam G VU over 2 yearsthis should be the accepted answer in my view
-
rivy over 2 years@chepner, @mwfearnley, To flexibly handle dot-files, an improvement would be: (1) remove last extension =>
echo "$(s=${filename:1}; echo -n ${filename:0:1}; echo ${s%.*})"
; (2) remove all extensions =>echo "$(s=${filename:1}; echo -n ${filename:0:1}; echo ${s%%.*})"
. -
jena over 2 years@GabrielStaples you could combine it with
dirname
, e.g.file=/home/jsmith/base.wiki; echo $(dirname $file)/$(basename $file .wiki)
-
JL Peyret over 2 yearsI went with this. The nice thing is that
basename
is made for this and I discovered a new capability in what it can do. -
Smeterlink over 2 yearsThe best answer so far!
-
Ravi OpenSource over 2 yearsthis worked for me