51

I'm using the find command in a ksh script, and I'm trying to retrieve just the filenames, rather than the full path. As in, I want it to return text.exe, not //severname/dir1/dir2/text.exe.

How would I go about getting that? To clarify, I know the directory the files are in, I am just grabbing the ones created before a certain date, so the pathname doesn't matter.

Null
  • 1,950
  • 9
  • 30
  • 33
Steve
  • 4,457
  • 12
  • 48
  • 89
  • 2
    possible duplicate of [How to only get file name with linux \`find\`?](http://stackoverflow.com/questions/5456120/how-to-only-get-file-name-with-linux-find) – Reinstate Monica Please Oct 23 '14 at 20:38
  • This has also been answered [here](http://stackoverflow.com/questions/5456120/how-to-only-get-file-name-with-linux-find). – sebastian Jan 07 '15 at 13:46
  • lots of duplicates for this one https://serverfault.com/questions/354403/remove-path-from-find-command-output – michael Nov 07 '17 at 06:16

6 Answers6

126

If you're using GNU find, then

find path -printf "%f\n"

will just print the file name and exclude the path.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
26
find ... -exec basename {} \; 

will also do the trick .. but as @Kent asks, why do you want this?

evil otto
  • 10,348
  • 25
  • 38
  • 1
    Using `-exec` like in this answer runs an external program for every single file found. @glennjackman's answer using `-printf` option can be much faster, depending on the number of files and the operating system. – Stéphane Gourichon Nov 15 '16 at 12:46
  • 2
    @StéphaneGourichon This is a good answer for non-GNU systems. There is no `-printf` in OpenBSD `find` – sheepdog Sep 21 '17 at 04:35
  • 1
    Also (but not necessarily applicable to non-GNU), there's a `basename -a` option that allows passing in multiple args, and a `find` option that allows passing in multiple args to the `-exec`, so if performance actually is an issue, this might help: `find ... -exec basename -a {} +` (note the `+` instead of `\;`) – michael Nov 07 '17 at 06:10
  • 3
    There are many reasons that someone might want to do this. Don't assume that you know everything. – SArcher Apr 09 '18 at 03:44
  • I found this question because I have a directory with hashed filenames `/XX/MD5HASH` and I am absolutely sure there is no duplicates. – wiktor Nov 27 '18 at 13:29
15

you can do it with:

find ..... |sed 's#.*/##'

however does it really make sense? if there are two files with same filename but located in different directories, how can you distinguish them?

e.g.

you are in /foo

/foo/a.txt
/foo/bar/a.txt

EDIT

edit the answer to gain some better text formatting.

As you described in comment, so you want to

  1. find some files,
  2. copy them to a dir,
  3. gzip them to an archive say a.gz
  4. remove copied files only if step 2 was successful

This could be done in one shot:

find ...|xargs tar -czf /path/to/your/target/a.gz 

this will find files, make a tar (a.gz) to your target dir.

SnoringFrog
  • 1,479
  • 1
  • 16
  • 30
Kent
  • 189,393
  • 32
  • 233
  • 301
  • Well, I'm using find because I'm trying to grab all the files created after a certain date, using -mtime. Is there a better way? – Steve Feb 08 '12 at 22:37
  • @Steve what are you gonna do with those files you found by 'find'? is there a particular requirement of removing the directory info? – Kent Feb 08 '12 at 23:14
  • 1
    yeah I'm going to be copying them to another directory and then doing stuff with them, so all I want is the actual filename. Otherwise, I'd have to run Find again. – Steve Feb 09 '12 at 14:27
  • @Steve what is "doing stuff" ? if it is something like archiving to tar, you could find..|xargs to tar then mv to your anotherDIR. anyway, the |sed... in my answer will cut the path info for you. – Kent Feb 09 '12 at 14:36
  • Sorry about that, 'doing stuff' is running gzip on it in the destination directory, and then if the copy operation was successful, deleting the original copy. The rationale was that I only want to delete the original if the copy was successful, so I have error checking between those steps in my script. – Steve Feb 09 '12 at 14:41
  • Disclaimer: I am fairly new to shell scripting. – Steve Feb 09 '12 at 15:00
  • @Steve see the EDIT in answer. – Kent Feb 09 '12 at 15:14
  • @kent - xargs won't work there unless there's a fairly small number of files. If there's "too many" then it will run the tar command multiple times, re-creating the archive each time. And you would probably want to make it a `.tar.gz` file or `.gz` I think what OP really wants as a one-shot is `zip -m` – evil otto Feb 09 '12 at 16:59
  • 1
    @evil, you are right about the xargs. then find -exec or find..-print | tar ...*.gz --files-from - should work. thx for pointing this out. – Kent Feb 09 '12 at 17:19
  • Maybe I can shed some light on this: Right now a client wants to know which files we already have and which he needs to transfer. The files (which for various reasons have unique names) are all placed in different subfolders, sometimes even with a different level of nesting etc. pp. With this command I can send the output to a txt file and send that to the customer. Job easy done. – flomei Mar 02 '18 at 13:08
7

Here's another answer.

find | awk -F/ '{print $NF}'    
Chaya Balram
  • 71
  • 1
  • 1
  • 1
    `NF` is the number of fields; `$NF` is the value of the last field. Its like doing `$3` if you knew there were 3 fields. – danwyand Nov 16 '18 at 17:39
5

GNU find natively supports this using -printf so all that you need to do is

find ... -printf '%f\n'

Update: Oops... this was already covered in the answer by @glenn-jackman which I somehow missed to see.

haridsv
  • 9,065
  • 4
  • 62
  • 65
0

The other answers have it covered but I'll give an example for a more complicated usecase that I didn't find a straightforward answer for.

In case you need both the full path and just the file name inside -exec, then you need to do this:

find ./k8s/charts/secrets/files/ \
    -type f \
    -name "*.yaml" \
    -exec sh -c \
    'vault decrypt "$1" --output "./k8s/charts/secrets/templates/$(basename "$1")"' _ {} \;

Here $1 will get the full path and $(basename "$1") will get just somefile.yaml. You have to do it inside sh -c because bash will expand once before find runs, not per file found.

https://www.shellcheck.net/wiki/SC2014

IceWreck
  • 117
  • 1
  • 2
  • 10