Move a group of files, prompting the user for confirmation for every file
Here is a small script (mv-confirm.sh
):
#!/bin/bash
# Just make sure there are at least one source and one destination
if [ "$#" -lt 2 ]; then
echo "Illegal number of parameters"
fi
DESTINATION=${*:$#} # last argument
SOURCES=( ${@:1:$#-1} ) # all but last arguments
for i in "${SOURCES[@]}"
do
read -p "move '$i' to '$DESTINATION' ? [y/n] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
echo mv "$i" "$DESTINATION"
fi
done
Sample use :
./mv-confirm.sh ** somewhere
move 'a' to 'somewhere' ? [y/n] n
move 'b' to 'somewhere' ? [y/n] y
mv b d
...
don't forget to make the script executable (chmod +x mv-confirm.sh)
.
NB
Since I don't want to mess with your files, I added echo
before the actual mv
command. Feel free to remove it for actually moving the files.
Way 1: In your graphical file browser, using a tree view
I assume your actual goal is to make a choice for each file, and not strictly speaking to be prompted. However, if you prefer to be prompted about each file in a terminal, see Way 2 below.
Most graphical file browsers support a tree view where you can view multiple levels of a directory hierarchy together. You can copy or move files out of this view, either individually or by selecting them and then operating on the whole selection. This works even with files from multiple folders.
In some file browsers, one must go into a Preferences menu to enable the tree view. In Caja, the MATE file browser, it's always enabled in the list view. Click View → List (or press Ctrl+2) to switch to the list view. Each directory has a small rightward-facing triangle to the left of it, indicating it can be expanded. To expand a folder, click that triangle, or press the → (right arrow) key when the folder is selected. In this example, I've selected them all one by one and am dragging them all at once their destination:
Assuming the source and destination are on the same drive, dragging moves by default. If you wanted to copy instead, you'd hold down the Ctrl key.
In theory, it's easy to fix mistakes when using this method, because most file browsers (including Caja) support Ctrl+Z to undo recent actions, including batch actions. However, I've had mixed luck with that. Sometimes files go back into the wrong folder.
The good news is that you appear to be working with mostly text documents, which tend to be small, so you can make an extra backup with tar
first without much hassle. I suggest doing that regardless of what method you use.
Way 2: The find
command with the -ok
action
It's common use find
to find files and pass their paths to an arbitrary external command with the -exec
and -execdir
actions. But find
also has -ok
and -okdir
that prompt before each time they run a command. If you really want to be prompted in a terminal about each file, I think this is the way to go.
When done to produce the same effects as shown above, that looks like this:
ek@Gnar:~$ find Texts -xtype f -ok mv -t 'Glorious Destination Folder' {} \;
< mv ... Texts/Robert Frost/New Hampshire.pdf > ? y
< mv ... Texts/Robert Frost/In The Clearing.pdf > ? n
< mv ... Texts/Helen Keller/The Story of My Life.pdf > ? y
< mv ... Texts/Saki/The Short Stories of Saki.pdf > ? y
< mv ... Texts/Jane Austen/Sense and Sensibility.pdf > ? n
< mv ... Texts/Jane Austen/Pride and Prejudice.pdf > ? y
< mv ... Texts/Richard Connell/The Stolen Crime.pdf > ? n
< mv ... Texts/Richard Connell/The Most Dangerous Game.txt > ? y
< mv ... Texts/Mary Wollstonecraft Shelley/Mathilda.epub > ? n
< mv ... Texts/Mary Wollstonecraft Shelley/Frankenstein: Or, The Modern Prometheus.pdf > ? y
< mv ... Texts/W. E. B. Du Bois/The Souls of Black Folk.pdf > ? n
< mv ... Texts/W. E. B. Du Bois/Darkwater: Voices from within the Veil.htm > ? y
Replace Texts
with the source directory (which could just be .
if you're cd
'd to it) and Glorious Destination Folder
with the actual destination directory.
This runs one command per file; unlike -exec
and -execdir
, -ok
and -okdir
don't support +
in place of \;
to pass multiple found paths in the same invocation of a command. Consequently, you don't need to use the -t dest
form of mv
. I suggest doing so anyway, though, both in general with find
and particularly in this situation so the prompts look more like the commands they're prompting about.
As that command is written, if find
encounters a symbolic link that ultimately points to a regular file, it (the link, not the target) is moved rather than ignored. If you really want to operate just one regular files, use -type f
instead of -xtype f
.
You may want to do this in a script
session so you can figure out what happened if you make a mistake. But again, no reason not to have a backup.
Way 3: Generate a manifest and edit it
At least so long as the files don't have newlines in their names, you can start by using a simple find
command to generate a manifest--a file that lists the names of the files you're (in this case potentially) interested in:
find Texts -xtype f >manifest
Replace manifest
with whatever name you like for the file that lists the files under consideration (and adjust the remaining directions accordingly).
As above, replace Texts
with your actual source directory, which could be .
if you've cd
'd to it. However, if Texts
is a relative path (including if it's .
) then you should make sure to be in the same location later when you actually perform the moves.
After running that find
command, open manifest
in a text editor. This could be nano
, vim
, emacs
, gedit
, pluma
, etc. In the editor, comment out the lines that name files you don't want to move by placing a #
at the beginning of them. Some editors will highlight those lines differently even when it doesn't recognize your file to be of any format it knows has comments. Most won't, though, and the real reason I suggest to do it this way is that you, as a human, are likely to have experience commenting lines out in configuration files (and perhaps in source code). I think this is more intuitive, and less error prone, than it would be to mark the lines you do want to move.
Of course, you could instead just delete the lines for files you don't want to move. The advantage of commenting them out is that it will be clear later what you did.
In my example, this is what the file's contents looked like after I edited it:
Texts/Robert Frost/New Hampshire.pdf
# Texts/Robert Frost/In The Clearing.pdf
Texts/Helen Keller/The Story of My Life.pdf
Texts/Saki/The Short Stories of Saki.pdf
# Texts/Jane Austen/Sense and Sensibility.pdf
Texts/Jane Austen/Pride and Prejudice.pdf
# Texts/Richard Connell/The Stolen Crime.pdf
Texts/Richard Connell/The Most Dangerous Game.txt
# Texts/Mary Wollstonecraft Shelley/Mathilda.epub
Texts/Mary Wollstonecraft Shelley/Frankenstein: Or, The Modern Prometheus.pdf
# Texts/W. E. B. Du Bois/The Souls of Black Folk.pdf
Texts/W. E. B. Du Bois/Darkwater: Voices from within the Veil.htm
Then you can filter out the commented lines with grep
and pipe the result to xargs
to pass them to mv
:
grep -Pv '^\s*#' manifest | xargs -d '\n' mv -t 'Glorious Destination Folder'
As above, replace Glorious Destination Folder
with the name of the actual destination. That's it; your files are moved. You can of course write echo
before mv
to see the commands that will be run first, if you like.
The way that command works is:
- The
grep
command outputs lines frommanifest
that don't (-v
) start (^
) with optional (*
) whitespace (\s
) followed by a literal#
character. The-P
flag select PCRE as the dialect of regular expressions (which enables\s
which I consider more readable than the traditional[[:space:]]
). - The
xargs
command divides its input (which is the output ofgrep
) into newline-terminated fields (-d '\n'
), then pastes each field (without the terminating newline) as a separate argument at the end of the commandmv -t 'Glorious Destination Folder'
, which it runs.