1

I observed a strange behavior cp. Exactly the point is that cp did not ask me for confirmation, even with the option interactive.

A test case looks like this

Existing files

find * -type f
app/file.txt
test/file.txt

correct behavior

/usr/bin/cp -ip test/file.txt app/
cp: overwrite app//file.txt (yes/no)? yes

incorrect behavior

find test/ -type f | while read line; do /usr/bin/cp -ip $line app/; done

Why in the second case cp didn't prompt.

2
  • 5
    Because stdin is not a terminal
    – jordanm
    Commented Feb 25, 2015 at 16:19
  • @jordanm Please post your answer as an answer.
    – derobert
    Commented Feb 27, 2015 at 17:02

1 Answer 1

1

Given the simplicity of your while loop, it makes more sense to use xargs for your task. It should also be faster, though I doubt your test/ directory would be large enough to notice.

find test/ -type f -print0 |xargs -o0 cp -ipt app/

Note that -t is a GNU extension. If that is problematic, you'd need something like this (to do this with GNU xargs, change the -J to a -I):

find test/ -type f -print0 |xargs -o0 -J % cp -ip % app/

I used find -print0 and xargs -0 so this will work even when your files have spaces in their names.

When xargs or the question's while loop run in this kind of manner, standard input is consumed and therefore interactivity with the terimal (tty, /dev/tty) is not guaranteed.

Using xargs -o reopens standard input as /dev/tty in each child process to allow for interactivity. Note, this is available in GNU xargs as well as BSD xargs but not Busybox xargs or other bare-bones POSIX xargs.

From the GNU findutils man page for xargs(1):

-o, --open-tty

Reopen stdin as /dev/tty in the child process before executing the command. This is useful if you want xargs to run an interactive application.

The -o option is an extension to the POSIX standard for better compatibility with BSD.

5
  • 1
    Even better would be to use find's -exec … + feature, which would actually solve OP's problem by leaving STDIN connected to the terminal.
    – derobert
    Commented Feb 27, 2015 at 17:01
  • @derobert – using -exec invokes the given command once per hit, which is grossly inefficient. xargs will actually batch the command to as few invocations as possible given command line length limits. This matters a lot when dealing with massive directory trees. The script's STDIN is not affected here since everything is assigned, so e.g. you will have no issues running INPUT="$(cat)" after this.
    – Adam Katz
    Commented Nov 23, 2019 at 18:03
  • @AdamKatz that's with a semicolon at the end, with a plus at the end, find passes as many as possible (limited by the maximum command line length set by the kernel) to each execution.
    – derobert
    Commented Nov 23, 2019 at 18:07
  • @KamilMaciorowski – xargs -o was the key. Thanks for the tip, I've updated my answer.
    – Adam Katz
    Commented Nov 24, 2019 at 23:21
  • @derobert – I've had bad luck with find … -exec in the past but I'll also admit that I don't fully understand it. The -exec command {} + variant is interesting; it likely preserves STDIN (now I understand what you meant) but it also gives up on trying to preserve the exit code (which perhaps makes sense since it's unclear which exit code to use when running in more than one batch). This would make for a viable second answer.
    – Adam Katz
    Commented Nov 24, 2019 at 23:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .