GTS Joe
GTS Joe

Reputation: 4182

Linux Find Command Hide Base Directory

Editor's note: This question is ambiguous, because it conflates two unrelated tasks:
(a) to print mere filenames (without path components) using the -printf action, and
(b) to pass the mere filename as an argument in the context of an -exec action via {}
(a) is mistakenly perceived as a way to implement (b).
This confusion has led to at least one answer focusing on (a).

I'm trying to use the find command to list all directories in a certain path, but hide that path in the output. The -printf "%P\n" flag is supposed to hide /path/to/directory/, but it's not working:

find /path/to/directory/* -maxdepth 0 -type d -printf "%P\n" -exec sudo tar -zcpvf {}.tar.gz {} \;

For example, the above command would create archives with:

/path/to/directory/dir1
/path/to/directory/dir2
/path/to/directory/dir3

How can I modify my command to output this:

dir1
dir2
dir3

Please note: I know I can do the above by cd /path/to/directory/ then using the find command, but it's important that I avoid using cd and do it all with the single find command.

Upvotes: 2

Views: 1709

Answers (2)

mklement0
mklement0

Reputation: 440132

Since you're only processing child directories (immediate subdirectories), a shell loop may be the simpler solution:

(cd "/path/to/dir" && for d in */; do sudo tar -zcpvf "${d%/}".tar.gz "$d"; done)

I know you want to avoid cd, but by enclosing the entire command in (…), it is run in a subshell, so the current shell's working dir. remains unchanged.

The remainder of this answer discusses how to solve the problem using GNU find.

The -execdir solution would work with BSD/OSX find too, and would actually be simpler there.


As for getting find's -printf to only print the filenames, without any directory components: use the %f format specifier:

find /path/to/dir/* -maxdepth 0 -type d -printf "%f\n"

This will print the mere names of all immediate subdirectories in the specified directory.

However, what you print has no effect on what {} expands to when using the -exec action: {} always expands to the path as matched, which invariably includes the path component specified as the input.

However, the -execdir action may give you what you want:

  • it changes to the directory at hand before executing the specified command

  • it expands {} to ./<filename> - i.e., the mere filename (directory name, in this case), prefixed with ./ - and passes that to the specified command.

Thus:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d -execdir sudo tar -zcpvf {}.tar.gz {} \;

Caveat: -execdir only behaves as described for files that are descendants of the input paths; for the input paths themselves, curiously, {} still expands to the input paths as-is[1] .

Thus, the command above does not use globbing (/path/to/dir/*) with -maxdepth 0, but instead uses /path/to/dir and lets find do the enumeration of contained items, which are than at level 1 - hence -maxdepth 1; since the input path itself should then not be included, -mindepth 1 must be added.

Note that the behavior is then subtly different: find always includes hidden items in the enumeration, whereas the shell's globbing (*) by default does not.


If the ./ prefix in the {} expansions should be stripped, more work is needed:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d \
  -execdir sh -c 'd=${1##*/}; sudo tar -zcpvf "$d".tar.gz "$d"' - {} \;

Involving the shell (sh) allows stripping the ./ prefix using a shell parameter expansion (${1##*/} would, in fact, strip any path component). Note the dummy argument -, which the shell assigns to $0, which we're not interested in here; {} then becomes shell parameter $1.


[1] With ./ prepended, if an input path is itself relative; note that BSD/OSX find does not exhibit this quirk: it always expands {} to the mere filename, without any path component.

Upvotes: 0

valch85
valch85

Reputation: 99

find  /path/to/directory/* -maxdepth 0 -type d -exec basename {} \;

find all directories find /path/to/directory/* -maxdepth 0 -type d

-exec basename {} \; - execute basename command with result parameters from find

Upvotes: 2

Related Questions