13

I'd like to remove a list of files and be asked for confirmation. The list is in the list.txt file. Why the following command doesn't work properly?

while read i; do rm -i $i; done < list.txt

The previous command doesn't wait for me but fortunately no file is deleted.

I'm using Bash.

2 Answers 2

20

rm -i reads its answer from standard input, which it inherits from the while loop. So a line of text from list.txt is being used as the answer to the "really remove?" question. To fix, you'll need to separate the two sources of input. The easiest way is to have read take its input from a separate file descriptor:

while IFS= read <&3 -r i; do
  rm -i -- "$i"     # Quote the file name, not just to be safe,
                    # but because it's the right syntax.
done 3< list.txt

You are no longer redirecting standard input (fd 0) for the while loop, so rm will read from the terminal as desired.

Alternatively, you could have done:

IFS=$'\n' # set field separator to newline
set -f    # disable globbing upon command substitution
rm -i -- $(cat list.txt)

Or, with GNU xargs:

xargs -rd '\n' --arg-file=list.txt rm -i --
2
  • 1
    So if I had in the list a file named y, the previous file would have been deleted?
    – user22304
    Oct 2, 2012 at 17:52
  • 5
    Not just y, but any name beginning with y. Apparently, rm just looks at the first character of whatever response it receives: 'y' is yes, anything else is no.
    – chepner
    Oct 2, 2012 at 17:57
2

For something a bit fancier than answering yes or no to each file, you could use iselect for interactive ncurses-based selection. e.g.

iselect -a -m < list.txt | xargs -d '\n' -r echo rm 

or to allow selection of files in the current dir:

find . -maxdepth 1 -type f | iselect -a -m | xargs -d '\n' -r echo rm

That will display a full-screen (full-terminal) ncurses-based listing, where files can be selected and de-selected at will by pressing SPACE. press ENTER to accept your selections (selected entries will be printed on stdout), or q to quit (nothing printed to stdout). press h for help.

Note the echo rm - i've written it like that for testing. change it to just rm once you're confident you understand what it's doing.

Also note that xargs is being used here with \n as the input separator. This will not work if any of the filenames have linefeed characters in them. For that matter, the iselect won't work properly either - it expects one item per line - u nfortunately, iselect does not have an option for null-terminated input or output.

iselect is packaged for debian, probably ubuntu and other distros too. home page is at http://www.ossp.org/pkg/tool/iselect/

You must log in to answer this question.