f



Bash: strip file path and file extension from a file name in a $string?

It appears that Bash supports stripping a match of $substring from $string 
from the front and from the back of $string through the following 
commands:

${string#substring}
${string%substring}

Yet, it doesn't appear that it is possible to simultaneously strip a 
couple of substrings from the front and from the back of a string, which 
is a bit of a problem if a user intends to strip the path and file 
extension from a file listing.  Nonetheless, this is certainly possible.

So, is it possible to strip both the path and the file extension from a 
file?  If so, can anyone provide an example?


Thanks in advance,
Rui Maciel
0
Rui
2/6/2011 3:04:38 PM
comp.unix.programmer 10848 articles. 0 followers. kokososo56 (350) is leader. Post Follow

13 Replies
6876 Views

Similar Articles

[PageSpeed] 6

On Sun, 06 Feb 2011 15:04:38 +0000
Rui Maciel <rui.maciel@gmail.com> wrote:

> It appears that Bash supports stripping a match of $substring from
> $string from the front and from the back of $string through the following 
> commands:
> 
> ${string#substring}
> ${string%substring}
> 
> Yet, it doesn't appear that it is possible to simultaneously strip a 
> couple of substrings from the front and from the back of a string, which 
> is a bit of a problem if a user intends to strip the path and file 
> extension from a file listing.  Nonetheless, this is certainly possible.
> 
> So, is it possible to strip both the path and the file extension from a 
> file?  If so, can anyone provide an example?

It's not possible. You have to put the result of the first removal in a
temporary variable, then perform the second removal on the temporary
variable.

See also http://mywiki.wooledge.org/BashFAQ/073
0
pk
2/6/2011 6:30:11 PM
Rui Maciel <rui.maciel@gmail.com> writes:
>It appears that Bash supports stripping a match of $substring from $string 
>from the front and from the back of $string through the following 
>commands:
>
>${string#substring}
>${string%substring}

_path=$(dirname "${string}")
_file=$(basename "${string}")
_ext=${string#${string%.*}}
_file_no_ext=$(basename "${string}" "${_ext}")

scott
0
scott
2/7/2011 10:42:07 PM
scott@slp53.sl.home (Scott Lurndal) writes:
> Rui Maciel <rui.maciel@gmail.com> writes:
>>It appears that Bash supports stripping a match of $substring from $string 
>>from the front and from the back of $string through the following 
>>commands:
>>
>>${string#substring}
>>${string%substring}
>
> _path=$(dirname "${string}")
> _file=$(basename "${string}")
> _ext=${string#${string%.*}}
> _file_no_ext=$(basename "${string}" "${_ext}")

Provided that a multi-step procedure is ok, a simpler (and probably
faster) way would be

[rw@sapphire]~ $echo $a
/home/rw/core.1
[rw@sapphire]~ $a=${a##*/}
[rw@sapphire]~ $a=${a%.*}
[rw@sapphire]~ $echo $a
core

Using a single, external tool (sed) in 'extreme leaning toothpick
syndrome' mode :-) was another option

[rw@sapphire]~ $echo $a
/home/rw/core.1
[rw@sapphire]~ $echo $a | sed 's/^\(.*\/\)\([^\/]*\)\(\.[^\/]*\)$/\2/'
core

The regex splits the input string into three parts, a possibly empty
leading one ending with a slash, a possibly empty trailing one
starting with a dot and not containing any slashes and an (also
possibly empty) 'middle part' which doesn't contain any slashes and
extends until the character in front of the last dot (if any).  The s
command then replaces the input string with the middle part.

NB: The sed approach obviously won't work for filenames containing
embedded newlines.
0
Rainer
2/8/2011 11:55:09 AM
Rainer Weikusat <rweikusat@mssgmbh.com> writes:

[...]

> [rw@sapphire]~ $echo $a | sed 's/^\(.*\/\)\([^\/]*\)\(\.[^\/]*\)$/\2/'
> core
>
> The regex splits the input string into three parts, a possibly empty
> leading one ending with a slash, a possibly empty trailing one
> starting with a dot

Unfortunately, it doesn't really do this. An alternate suggestion
which fixes some of the problems the expression above has would be

        sed 's/\.[^/.]*$//; s/^.*\///'
0
Rainer
2/8/2011 12:06:38 PM
On 02/ 7/11 04:04 AM, Rui Maciel wrote:
> It appears that Bash supports stripping a match of $substring from $string
> from the front and from the back of $string through the following
> commands:
>
> ${string#substring}
> ${string%substring}
>
> Yet, it doesn't appear that it is possible to simultaneously strip a
> couple of substrings from the front and from the back of a string, which
> is a bit of a problem if a user intends to strip the path and file
> extension from a file listing.  Nonetheless, this is certainly possible.
>
> So, is it possible to strip both the path and the file extension from a
> file?  If so, can anyone provide an example?

Reading the answers to this question I have to wonder why bash lacks the 
modifiers [h,r,t] provided by csh?

So I guess one answer is use csh!

-- 
Ian Collins
0
Ian
2/8/2011 8:33:19 PM
On Sun, 06 Feb 2011 18:30:11 +0000, pk wrote:

>> So, is it possible to strip both the path and the file extension from a 
>> file?  If so, can anyone provide an example?
> 
> It's not possible. You have to put the result of the first removal in a
> temporary variable, then perform the second removal on the temporary
> variable.

Or use command substitution, e.g.:

	filename=/path/to/file.ext
	basename=$(ff=${filename%.ext} ; echo ${ff##*/})

But that's rather heavy-weight, as bash will fork() a child process for
the command.

0
Nobody
2/9/2011 6:06:19 AM
Scott Lurndal wrote:

> _path=$(dirname "${string}")
> _file=$(basename "${string}")
> _ext=${string#${string%.*}}
> _file_no_ext=$(basename "${string}" "${_ext}")

I see that your suggestion works.  Nonetheless, it appears it relies on a 
hand full of calls to external programs to perform a task which amounts to 
nothing more than basic string handling.  Isn't there a solution that's 
more elegant and doesn't rely on external programs?


Rui Maciel
0
Rui
2/9/2011 11:59:01 AM
pk wrote:

> It's not possible. You have to put the result of the first removal in a
> temporary variable, then perform the second removal on the temporary
> variable.

Sounds reasonable, although a bit disappointing.  It would be great if it 
was possible to strip a file name from it's full path and file extension 
through a single built-in command.


Thanks for the help,
Rui Maciel
0
Rui
2/9/2011 12:03:55 PM
Rui Maciel <rui.maciel@gmail.com> writes:
> Scott Lurndal wrote:
>
>> _path=$(dirname "${string}")
>> _file=$(basename "${string}")
>> _ext=${string#${string%.*}}
>> _file_no_ext=$(basename "${string}" "${_ext}")
>
> I see that your suggestion works.  Nonetheless, it appears it relies on a 
> hand full of calls to external programs to perform a task which amounts to 
> nothing more than basic string handling.  Isn't there a solution that's 
> more elegant and doesn't rely on external programs?

----------------------
#!/bin/sh

IFS=/
set -- $1
while test -n "$2"; do shift; done

IFS=.
set -- $1
name="$1"
while test -n "$2"; do shift; name="${name}.$1"; done

echo "$name"
----------------------

[SCNR].
0
Rainer
2/9/2011 5:03:44 PM
2011-02-08, 11:55(+00), Rainer Weikusat:
> scott@slp53.sl.home (Scott Lurndal) writes:
>> Rui Maciel <rui.maciel@gmail.com> writes:
>>>It appears that Bash supports stripping a match of $substring from $string 
>>>from the front and from the back of $string through the following 
>>>commands:
>>>
>>>${string#substring}
>>>${string%substring}
>>
>> _path=$(dirname "${string}")
>> _file=$(basename "${string}")
>> _ext=${string#${string%.*}}
>> _file_no_ext=$(basename "${string}" "${_ext}")
>
> Provided that a multi-step procedure is ok, a simpler (and probably
> faster) way would be
>
> [rw@sapphire]~ $echo $a
> /home/rw/core.1
> [rw@sapphire]~ $a=${a##*/}
> [rw@sapphire]~ $a=${a%.*}
> [rw@sapphire]~ $echo $a
[...]

Beware of the ${##/%} approaches with strings like "/foo/bar/",
"/" or "foo"  which basename/dirname generally handle better.

$ dirname foo
..
$ basename /foo/bar/
bar
$ dirname /
/


Also there are some missing "--" above

$ string="--foo--/bar"
$ dirname "$string"
dirname: invalid option -- '-'
Try `dirname --help' for more information.
$ dirname -- "$string"
--foo--

And as usual with command substitution, it strips trailing
newline characters even when it shouldn't

-- 
Stephane
0
2/13/2011 10:09:14 PM
2011-02-09, 17:03(+00), Rainer Weikusat:
> Rui Maciel <rui.maciel@gmail.com> writes:
>> Scott Lurndal wrote:
>>
>>> _path=$(dirname "${string}")
>>> _file=$(basename "${string}")
>>> _ext=${string#${string%.*}}
>>> _file_no_ext=$(basename "${string}" "${_ext}")
>>
>> I see that your suggestion works.  Nonetheless, it appears it relies on a 
>> hand full of calls to external programs to perform a task which amounts to 
>> nothing more than basic string handling.  Isn't there a solution that's 
>> more elegant and doesn't rely on external programs?
>
> ----------------------
> #!/bin/sh
>
> IFS=/
> set -- $1
> while test -n "$2"; do shift; done
[...]

what about /foo//bar/baz, foo/*/bar

-- 
Stephane
0
2/13/2011 10:11:56 PM
2011-02-09, 09:33(+13), Ian Collins:
> On 02/ 7/11 04:04 AM, Rui Maciel wrote:
>> It appears that Bash supports stripping a match of $substring from $string
>> from the front and from the back of $string through the following
>> commands:
>>
>> ${string#substring}
>> ${string%substring}
>>
>> Yet, it doesn't appear that it is possible to simultaneously strip a
>> couple of substrings from the front and from the back of a string, which
>> is a bit of a problem if a user intends to strip the path and file
>> extension from a file listing.  Nonetheless, this is certainly possible.
>>
>> So, is it possible to strip both the path and the file extension from a
>> file?  If so, can anyone provide an example?
>
> Reading the answers to this question I have to wonder why bash lacks the 
> modifiers [h,r,t] provided by csh?
>
> So I guess one answer is use csh!

Use zsh instead.

-- 
Stephane
0
2/13/2011 10:12:39 PM
Stephane CHAZELAS <stephane_chazelas@yahoo.fr> writes:
> 2011-02-09, 17:03(+00), Rainer Weikusat:
>> Rui Maciel <rui.maciel@gmail.com> writes:
>>> Scott Lurndal wrote:
>>>
>>>> _path=$(dirname "${string}")
>>>> _file=$(basename "${string}")
>>>> _ext=${string#${string%.*}}
>>>> _file_no_ext=$(basename "${string}" "${_ext}")
>>>
>>> I see that your suggestion works.  Nonetheless, it appears it relies on a 
>>> hand full of calls to external programs to perform a task which amounts to 
>>> nothing more than basic string handling.  Isn't there a solution that's 
>>> more elegant and doesn't rely on external programs?
>>
>> ----------------------
>> #!/bin/sh
>>
>> IFS=/
>> set -- $1
>> while test -n "$2"; do shift; done
> [...]
>
> what about /foo//bar/baz, foo/*/bar

You are aware of a concept called 'a joke'?
0
rweikusat (2830)
2/14/2011 1:14:59 PM
Reply: