0

I want to find a directory, apply a command to this and the result should be piped into this found directory.

Assuming that find -type d gives

. ./cortexa53-crypto-mx8mp ./cortexa53-crypto ./imx8mp_sp ./all 

I think about doing something like that:

find -type d -exec '~/bin/opkg-utils/opkg-make-index {} | gzip > {}/package.gz' \; 

the result is as example:

find: '~/bin/opkg-utils/opkg-make-index ./cortexa53-crypto-mx8mp | gzip > ./cortexa53-crypto-mx8mp/package.gz': No such file or directory 

But if I execute that command (so what is inside ' ') - it works !?!?

Bonus question: howto avoid finding of .?

1 Answer 1

2

If you need to execute anything more complex than a simple command with argument from -exec, then do so within an in-line script:

find . -type d -exec sh -c ' tmpfile=$(mktemp) || exit trap "rm -f \"\$tmpfile\"" EXIT for dirpath do "$HOME"/bin/opkg-utils/opkg-make-index "$dirpath" | gzip -c >"$tmpfile" && cp -- "$tmpfile" "$dirpath"/package.gz done' sh {} + 

This passes batches of directory paths as arguments to the in-line sh -c script. This short script loops over these paths and for each it will call your utility and write the compressed output to a temporary file. After writing the file, it is moved into the directory and the loop continues with the next directory.

Note that this would recurse into subdirectories.

To avoid finding . use ! -path .:

find . ! -path . -type d -exec sh -c '...' sh {} + 

Or, with GNU find (and some others), use -mindepth 1:

find . -mindepth 1 -type d -exec sh -c '...' sh {} + 

To avoid recursing into subdirectories, use -prune as soon as you've found a directory:

find . ! -path . -type d -prune -exec sh -c '...' sh {} + 

Or, with GNU find, use -maxdepth 1:

find . -mindepth 1 -maxdepth 1 -type d -exec sh -c '...' sh {} + 

But if you're only interested in a single directory, without recursion, then you may instead use just a shell loop:

shopt -s nullglob dotglob tmpfile=$(mktemp) || exit trap 'rm -f "$tmpfile"' EXIT for dirpath in */; do dirpath=${dirpath%/} [ -h "$dirpath" ] && continue "$HOME"/bin/opkg-utils/opkg-make-index "$dirpath" | gzip -c >"$tmpfile" && cp -- "$tmpfile" "$dirpath"/package.gz done 

This is essentially the same loop as in the in-line script that find executes, but it's a bash script and it does needs to carry out some of the work that find would otherwise perform (enable globbing hidden names, check that $dirpath is not a symbolic link etc.)

5
  • Note that in POSIX sh, you can use ~. You'd want to check the exit status of mktemp before carrying on with using tmpfile. Switching to ksh93 / zsh / bash... and enabling pipefail would allow you detect the failures of opkg-make-index (and make the using of a tempfile more relevant). You may want to report failurs in sh's exit status so it's reported by find as well. Note that if the last gzip fails or the command is interrupted, that will leave a tempfile around. Commented Aug 27, 2021 at 7:32
  • Also note that mktemp creates a new temp file for you to use safely, but if you rename or delete it, reusing the same path is not safe any longer as some other user could recreate the same file as a symlink to somewhere else. You'd need to call mktemp each time in the loop or switch to shells that can create tempfiles (zsh, ksh93...). See also ksh93 >; redirection operator here. Commented Aug 27, 2021 at 7:39
  • @StéphaneChazelas I just thought about that. Using cp rather than mv would be better I suppose (or call mktemp in each iteration as you suggest). Commented Aug 27, 2021 at 7:41
  • Thanks for answering this and the details around! Originally I want to solve that in a single line or at least understand why my attempts fail. But maybe sometime you have to be pragmatic ;) Commented Aug 29, 2021 at 15:03
  • @Arno You can definitely put all of that code into a single line if you want, but I don't really see the point of it. Just use ; in place of most of the newline characters in the in-line script (not after do though). Commented Aug 29, 2021 at 15:48

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.