System: Ubuntu MATE 18.04.
I have a collection of files (in multiple directories in a directory structure) and I want to move many of them to another directory. I can tell which files I want to move by their names (they are recipe titles), but there is no other way to distinguish between files I do want to move and files I don't want to move; I need to make a decision for every file by examining the name myself.
So, I am running a command once for each file I want to move, like
mv Snacks/OkaraCrackers NeedsTesting/
mv Snacks/SunflowerBrittle NeedsTesting/
mv Treats/ChocolateChilliFudge NeedsTesting/
mv Treats/PecanBlondies NeedsTesting/
Even with tab completion this is toooo much typing. What I really want to do is something like
shopt -s globstar
mv --prompt-me-for-every-file ** NeedsTesting/
So I can just press y or n for each file.
How can I move a group of files and be prompted for confirmation for every file?
Here is a small script (
mv-confirm.sh
):Sample use :
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 actualmv
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
actionIt's common use
find
to find files and pass their paths to an arbitrary external command with the-exec
and-execdir
actions. Butfind
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:
Replace
Texts
with the source directory (which could just be.
if you'recd
'd to it) andGlorious 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 ofmv
. I suggest doing so anyway, though, both in general withfind
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: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'vecd
'd to it. However, ifTexts
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, openmanifest
in a text editor. This could benano
,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:
Then you can filter out the commented lines with
grep
and pipe the result toxargs
to pass them tomv
:As above, replace
Glorious Destination Folder
with the name of the actual destination. That's it; your files are moved. You can of course writeecho
beforemv
to see the commands that will be run first, if you like.The way that command works is:
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:]]
).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.