2

I got access to only Busybox 1.31.1

I originally wanted to remove the current working directory of my output (the single dot).

Example:

/prueba$ ls
uno dos tres

When I:

$ busybox find .
.
./uno
./dos
./tres

That's easily done with either:

busybox find . -not -path .
busybox find . -mindepth 1

Now, what I tried before is:

busybox find . -exec sh -c ' readlink -f "$1" | tail -n +2 ' sh {} \;

Which prints nothing. If verbose output is activated:

==> standard input <==
==> standard input <==
==> standard input <==
==> standard input <==
==> standard input <==

Completely different output if the line address is one:

busybox find . -exec sh -c ' readlink -f "$1" | tail -n +1 ' sh {} \;
==> standard input <==
/tmp/prueba
==> standard input <==
/tmp/prueba/tres
==> standard input <==
/tmp/prueba/dos
==> standard input <==
/tmp/prueba/uno

What's going on?

abacox
  • 143
  • I suppose you mean -mindepth 1, not -maxdepth 1, since the . given on the command line is at depth 0. But is there a particular reason to use tail and not the two ways you already have for removing . from the output of find? – ilkkachu Sep 09 '21 at 14:14
  • @ilkkachu Hello. Yes, I'll will edit my post. No, there is no particular reason to use tail I was using it due to ignorance, I tought that was the way to go, but after knowing that find can do it by itself I was just curious why I wasn't getting the output I wanted. – abacox Sep 12 '21 at 02:15
  • yep, just wondering. Namely, find . -not -path . -exec ... should work too. – ilkkachu Sep 12 '21 at 10:35
  • I was told -not is non-standard, better to use ! which is standard complaint. Any literature work you can recommend to know the "right" way? – abacox Sep 13 '21 at 00:10
  • 1
    right, ! is the standard one. Though just based on the man pages, the GNU, FreeBSD, OpenBSD and Busybox versions support -not too. (Plus -and and -or which are similarly nonstandard variants of -a and -o.) In this case, the GNU manpage even mentions those are not POSIX compliant. The specification is online too, but as you might guess with a standard, it's not always the most fluent read. – ilkkachu Sep 13 '21 at 07:44

1 Answers1

3

The

-exec some command with parms {} \;

invokes the command once for each file. In your case the readlink outputs one line of output, and then your tail -n +2 strips off the first line, leaving you with nothing. If you use tail -n +1 this is just a verbose way of saying cat, to copy the input to output.

If you rewrite it so the tail -n +2 is outside the implicit loop like this

busybox find . -exec sh -c ' readlink -f "$1" ' sh {} \; | tail -n +2

you will get your expected result.

You can make the command more efficient by batching the command execution.

busybox find . -exec readlink -f -- {} + | tail -n +2

This requires the command you want to run taking multiple files. The + rather than the \; makes find run the command many fewer times as it runs the command only when it has a full buffer of filenames rather than once per file. Obviously I have also removed the unneeded sh as well.

icarus
  • 17,920
  • Thank you very much, I'm amazed on how quickly a clear and well done explanation can be provided within minutes. Internet is awesome, you guys are awesome!

    About the unneeded sh . I did because I tend to work with multiple files with non ASCII names and with spaces as well. So I followed this advise, just to be on the safe side:

    https://unix.stackexchange.com/a/156010

    – abacox Sep 08 '21 at 05:43
  • 1
    The point about spaces and other 'interesting' characters in names is that they are a problem for the shell. Remove the extra shell and the issue with spaces goes away. I added an extra -- to the answer so filenames like -m don't cause problems. – icarus Sep 09 '21 at 14:08
  • What -- does? Is it for find or for readlink? – abacox Sep 12 '21 at 02:58
  • 1
    -- is a standard convention to indicate the end of options. This allows you to distinguish between a file called -i and an option -i. In this case it is for readlink. – icarus Sep 12 '21 at 07:10
  • 1
    Strictly speaking, while it's good practice to use -- and never harms, here it's not strictly necessary as all arguments will start with ./ – Stéphane Chazelas Sep 12 '21 at 07:40
  • 1
    @StéphaneChazelas Totally agree. The problem only arises if . is replaced with a list of files. The most likely way that an unsuspecting person would generate such a list is with filename expansion. I haven't seen the advice recently but at one time there was a suggestion to have a file called -i so if you typed rm * and the filename came first or your version of argument parsing sorted options to the front you would get some warning that you were about to delete more than you expected. – icarus Sep 12 '21 at 14:56
  • 2
    @icarus, yes, there's (or at least was) such a -i file in the source tarball of bash for that reason. Note that -- is necessary with -execdir with some implementations of find. – Stéphane Chazelas Sep 12 '21 at 17:07