Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

How can I get only the filename using sed? I've this

out_file=$(echo $in_file|sed "s/\(.*\.\).*/\1mp4/g")

But I get the path too /root/video.mp4, and I want only video.mp4.

share|improve this question

4 Answers 4

up vote 4 down vote accepted

basename from the GNU coreutils can help you doing this job:

$ basename /root/video.mp4
video.mp4

If you already know the extension of the file, you can invoke basename using the syntax basename NAME [SUFFIX] in order to remove it:

$ basename /root/video.mp4 .mp4
video

Or another option would be cutting everything after the last dot using sed:

$ basename /root/video.old.mp4 | sed 's/\.[^.]*$//'
video.old
share|improve this answer
1  
Using sed 's/\.[^.]*$//' as you have, will fail for (hidden) .filename and . and .. directories –  Peter.O Aug 4 '12 at 13:47

Use any of the followings ways:

out_file="${in_file##*/}"

out_file="$(basename $in_file)"

out_file="$(echo $in_file | sed 's=.*/==')"

out_file="$(echo $in_file | awk -F"/" '{ print $NF }')"

ps. You get the same string because in your statement \(.*\.\) matches to the string from the beginning until dot (/root/video.) and then you manually add .mp4 which is the same as in you original string. You should use s=.*\([^/]*\)=\1= instead.

Update:

To get the only filename without extension you can :

out_file="$(echo $in_file | sed 's=.*/==;s/\.[^.]*$/.new_ext/'

out_file="$(echo $in_file | sed 's=\([^/]*\)\.[^./]*$=\1.new_ext=')"

out_file="$(echo $in_file | awk -F"/" '{ gsub (/\.[^/.]*$/,".new_ext",$NF);print $NF }'
share|improve this answer
    
But with any of those methods I get filename with the format and I need to get only the filename and put a new format manually. –  Shixons Aug 4 '12 at 6:30
    
Ah, that makes sense. I've updated my answer. –  rush Aug 4 '12 at 6:43
    
@rush: There will be edge cases, e.g. for a file named my.file.tar.gz. –  donothingsuccessfully Aug 4 '12 at 7:02
    
Thanks, that worked :). –  Shixons Aug 4 '12 at 7:08
    
@donothingsuccessfully there was a missing dot symbol in the last sed and awk. Fixed. Thank you. –  rush Aug 4 '12 at 7:09

One of the fundamentals of using regex is that patterns are greedy by nature when specifying the wild card. While the answer proposed by @uloBasEI is certainly a working answer, it also requires the use of the basename command. The original question from @Shixons requests a solution using only sed.

Before continuing, it's always helpful to know which version of sed is the target. I'm assuming BSD (as shipped with OSX).

First of all, the pattern proposed in the original question doesn't work because it captures everything from the beginning of the input string up to and including the last dot. Without anchors, this search will swallow up everything from left to right. The "/1" matched pattern, therefore, is everything up to and including the last dot. Even a filename with multiple dots will be swallowed up whole. Not the desired result at all.

The first step is to establish a strategy for identifying patterns. Here, you'd like to get rid of everything to the left of the filename (we'll deal with the extension later):

out_file="$(echo $in_file | sed 's/^\(\/.*\/\)*.*/\1/')"

The search matches from the beginning of the string. It matches a pattern of "/.*" zero or more times and deletes everything afterwards. We print the matched patterns with "\1". We are not searching globally; we are searching from the beginning of the string by specifying the ^ anchor.

We get better clarity by enabling the "-E" option so we don't have to escape parentheses:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*.*/\1/')"

So now we have the part on the left. Let's add the part on the right. Note that we need to keep the left part as a pattern because that's how we can specify that it appears zero or more times. All we do now is add a pattern for the part on the right:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)/\2/')"

We only print out the second match, thereby discarding everything but the filename. But we still need to remove the filename extension.

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2/')"

The "$" at the end is optional.

Finally, to add the new extension you just revise like so:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2.mp4/')"

An additional optimization is to make the first forward slash optional to handle relative paths:

out_file="$(echo $in_file | sed -E 's/^([\/]?.*\/)*(.*)\..*$/\2.mp4/')"

I came across this question by being lazy while looking for a sed pattern to replace basename. I'm working on a stripped system that doesn't have that command installed.

share|improve this answer

The easiest solution is remove everything until last appearance of /:

echo /root/video.mp4 | sed 's/.*\///'

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.