This is not as easy as it may seem.
Analysis
When running cp -i
and answering it prompts interactively, the newline you get after typing yEnter comes from the line discipline. The line discipline echoes y
and the newline.
You can observe this mechanism by running sole cat
in a terminal. When cat
awaits input, you can type a long line and even erase characters (with Backspace) or the whole line (with Ctrl+u); everything you see is handled by the line discipline, cat
just sits there without any input. Only when you hit Enter (or Ctrl+m, or Ctrl+j), cat
gets the line and prints it. The newline after what you typed is from the line discipline, the newline after what cat
printed is from cat
.
Or you can type something and hit Ctrl+d to send it to cat
without a newline. Then there will be no newline after what you typed and no newline after what cat
printed.
Similarly, when prompted by cp -i
, you can interactively type yCtrl+dCtrl+d (why twice? here), cp
will accept y
and you will see y
echoed by the line discipline, but no newline (because you did not type one). cp
itself does not print a newline here. Normally (i.e. when you do type Enter) what you see looks right (i.e. with newlines in the right places) because of the line discipline. We can say cp
expects a line discipline that injects newline characters where appropriate and thus makes the output look nice.
The point is there is a line discipline between your keyboard and cp -i
, it echoes what you type, including a newline upon Enter.
In your yes | cp -i …
, cp
gets input almost as if you typed yEnter via a line discipline, but this time there is nothing like a line discipline that would echo what enters cp
. This is the reason you observed no newlines (and no y
characters).
In the case of yes '' | cp -i …
, the newlines after not overwritten
were actually printed by cp
. What's missing is a newline before each not overwritten
. Without yes
, if you just hit Enter in response to the prompt, you would see not overwritten
in a separate line.
Towards solution
To get what you want, you need something that echoes the input that goes into cp
. At first glance it seems it may be an actual line discipline (few ideas here: How to trick a command into thinking its output is going to a terminal; note we don't want to trick cp
, we want a "side effect" of having a terminal) or tee
between yes
and cp
, like this:
# both flawed
yes | socat STDIO EXEC:'cp -i …',pty
yes | tee >/dev/tty | cp -i …
The above will not work well because yes
generates its output immediately and as much as possible, it's like a user who mashes yEnter no matter if prompted or not. Echoing this makes the output look way worse than what you posted in the question.
Even if you knew in advance you needed just one yEnter, using echo y
instead of yes
would also not work well, because most likely the input would be seen and printed (by the added line discipline or by tee
) before cp
prints its prompt.
Solution
A proper solution is to run cp -i …
with additional pty, but only give it input when prompted. expect(1)
can do it. This is a quick and dirty expect
script:
#!/usr/bin/expect -f
set str [lindex $argv 0]
spawn -noecho cp -i {*}[lrange "$argv" 1 end]
while 1 {
expect {
"overwrite *\\?" { send "$str\r" }
eof exit
}
}
Users with localized cp
should adjust the "overwrite *\\?"
pattern.
Be advised I have little experience with expect
, the script may be sub-optimal or even somewhat flawed. Treat it as a proof of concept. Save the script as cpx
in a directory in your $PATH
, make it executable (chmod +x cpx
) and use like this:
cpx y ?.? smalls
# or
cpx n ?.? smalls
In practice it may be good to define shell aliases:
alias cpy='cpx y'
alias cpn='cpx n'
and use them like this:
cpy ?.? smalls
# or
cpn ?.? smalls