154

I am trying to use awk to get the name of a file given the absolute path to the file.
For example, when given the input path /home/parent/child/filename I would like to get filename I have tried:

awk -F "/" '{print $5}' input

which works perfectly.
However, I am hard coding $5 which would be incorrect if my input has the following structure:

/home/parent/child1/child2/filename

So a generic solution requires always taking the last field (which will be the filename).

Is there a simple way to do this with the awk substr function?

1
  • 3
    as someone pointed out using basename is the official way of doing this, using awk for this is not good to put it lightly. :D
    – Kashyap
    Commented Jun 3, 2016 at 22:29

10 Answers 10

313

Use the fact that awk splits the lines in fields based on a field separator, that you can define. Hence, defining the field separator to / you can say:

awk -F "/" '{print $NF}' input

as NF refers to the number of fields of the current record, printing $NF means printing the last one.

So given a file like this:

/home/parent/child1/child2/child3/filename
/home/parent/child1/child2/filename
/home/parent/child1/filename

This would be the output:

$ awk -F"/" '{print $NF}' file
filename
filename
filename
3
  • 5
    It works perfectly. I didnt think about $NF variable. Thanks for your immediate simple and effective answer. Commented Jul 29, 2013 at 10:27
  • 20
    For the penult one, use $(NF-1).
    – Itachi
    Commented Aug 12, 2018 at 4:08
  • 2
    Was writing a script to work with some docker commands and this did the trick. Thanks!
    – Harlin
    Commented Apr 11, 2019 at 21:26
40

In this case it is better to use basename instead of awk:

 $ basename /home/parent/child1/child2/filename
 filename
1
  • 6
    @Mari F.Y.I dirname does the opposite and strips file not the path. Commented Jul 29, 2013 at 10:34
7

If you're open to a Perl solution, here one similar to fedorqui's awk solution:

perl -F/ -lane 'print $F[-1]' input

-F/ specifies / as the field separator
$F[-1] is the last element in the @F autosplit array

2
  • 1
    Thanks. This helped me to get last but one field. perl -F"/" -lane 'print $F[-2]' input
    – riderchap
    Commented Oct 1, 2015 at 13:58
  • 1
    this worked for me when receiving from piped input. Totally forgot about perl.
    – Chris
    Commented May 23, 2018 at 21:46
5

Another option is to use bash parameter substitution.

$ foo="/home/parent/child/filename"
$ echo ${foo##*/}
filename
$ foo="/home/parent/child/child2/filename"
$ echo ${foo##*/}
filename
5

Like 5 years late, I know, thanks for all the proposals, I used to do this the following way:

$ echo /home/parent/child1/child2/filename | rev | cut -d '/' -f1 | rev
filename

Glad to notice there are better manners

3

It should be a comment to the basename answer but I haven't enough point.

If you do not use double quotes, basename will not work with path where there is space character:

$ basename /home/foo/bar foo/bar.png
bar

ok with quotes " "

$ basename "/home/foo/bar foo/bar.png"
bar.png

file example

$ cat a
/home/parent/child 1/child 2/child 3/filename1
/home/parent/child 1/child2/filename2
/home/parent/child1/filename3

$ while read b ; do basename "$b" ; done < a
filename1
filename2
filename3
0
3

I know I'm like 3 years late on this but.... you should consider parameter expansion, it's built-in and faster.

if your input is in a var, let's say, $var1, just do ${var1##*/}. Look below

$ var1='/home/parent/child1/filename'
$ echo ${var1##*/}
filename
$ var1='/home/parent/child1/child2/filename'
$ echo ${var1##*/}
filename
$ var1='/home/parent/child1/child2/child3/filename'
$ echo ${var1##*/}
filename
0

you can skip all of that complex regex :

 echo '/home/parent/child1/child2/filename' | 

 mawk '$!_=$-_=$NF' FS='[/]'
                                  filename

2nd to last :

 mawk '$!--NF=$NF' FS='/'
                           child2

3rd last field :

 echo '/home/parent/child1/child2/filename' | 

 mawk '$!--NF=$--NF' FS='[/]'
 
                    child1

4th-last :

 mawk '$!--NF=$(--NF-!-FS)' FS='/'

 echo '/home/parent/child000/child00/child0/child1/child2/filename' |
                                     child0
 echo '/home/parent/child1/child2/filename'
             parent

major caveat :

- `gawk/nawk` has a slight discrepancy with `mawk` regarding 

   - how it tracks multiple,
   - and potentially conflicting, decrements to `NF`, 

   - so other than the 1st solution regarding last field, 
   - the rest for now, are only applicable to `mawk-1/2`
0

just realized it's much much cleaner this way in mawk/gawk/nawk :

echo '/home/parent/child1/child2/filename' | …

'

awk ++NF FS='.+/' OFS=      # updated such that 
                            # root "/" still gets printed

'

filename
-1

You can also use:

    sed -n 's/.*\/\([^\/]\{1,\}\)$/\1/p'

or

    sed -n 's/.*\/\([^\/]*\)$/\1/p'
1
  • 3
    At this point nobody's upvoted this answer, probably because it's comparably fugly :) Commented Oct 1, 2015 at 7:29

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