Reputation: 4182
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
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