1

I am trying to remove files from a directory by pasting their names into a while loop that will simply rm each item from the list of files given to it.

$ sponge | while read file; do echo "rm $file"; rm "$file"; done;
background.bundle.js
964.bundle.js
background.bundle.js.LICENSE.txt
...
META-INF/manifest.mf
META-INF/mozilla.sf
META-INF/mozilla.rsa
^D
rm background.bundle.js
rm: remove regular file ‘background.bundle.js’? $
$

(Notice the ^D at the end)

As shown, after issuing the ^D (end-of-file), the sponge command sends the entire output to the while loop, and rm is invoked for each file separately. However, what happens is that it gets invoked once for background.bundle.js, asks for confirmation, and without allowing me the opportunity to enter anything, the loop exits prematurely. This leaves the prompt character ($) without the necessary newline that should follow it, indicating an abrupt end to the loop.

I am looking to fix this issue without resorting to temporary files. I want to manage file input using exec so that the loop reads from a different input stream, leaving standard input available for rm to read the user’s confirmation. How can I redirect sponge output and read input to a specific file descriptor for this purpose?

4
  • 2
    Misusing of sponge? What is the need of it here? Commented Nov 14, 2024 at 18:00
  • If you're sure you want them removed, change rm to rm -fv, but I'm also curious about sponge in this case. Commented Nov 14, 2024 at 18:46
  • This looks like the while ...; do xxx; done loop is being executed in a sub-shell whose stdin is piped from the sponge command rather than from the keyboard. Commented Nov 15, 2024 at 4:45
  • for those curious about sponge, it will force the pipe to wait for the whole input to be pasted before starting to process it. Without sponge, the output of rm, echo and my own pasting would be all intertwined. Commented Nov 18, 2024 at 10:14

1 Answer 1

2

If the intention is to remove the files without confirmation when rm is actually an alias or a function similar to this:

alias rm='/bin/rm -i'

Then using the command(1p) utility will bypass the alias/function:

sponge |
while IFS= read -r file; do
    printf "rm %s\n" "$file"
    command rm "$file"
done

If the intention is to force input from the terminal in a subshell, you can use a solution which feels a bit "hacky": reassign only the stdin of rm(1) from /dev/tty (which is the device representing the controlling terminal for the current process):

sponge |
while IFS= read -r file; do
    printf "rm %s\n" "$file"
    rm "$file" </dev/tty       # rm is an alias to '/bin/rm -i'
done

Notes:

  1. The recommended idiom for using read(1p) with while is while IFS= read -r ...; do ...; done.
  2. printf(1p) is recommended over echo(1p): Why is printf better than echo?.
3
  • 1
    Thank you for your suggestion. A simple rm "$file" < /dev/tty did the trick. No need to save the output of the tty command. Commented Nov 18, 2024 at 10:12
  • @ychaouche Of course, /dev/tty is the device representing the controlling terminal for the current process. Commented Nov 18, 2024 at 11:26
  • I updated my answer, simplifying it. Commented Nov 18, 2024 at 11:36

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.