Never, ever use for foo in $(cat bar)
. This is a classic mistake, commonly known as bash pitfall number 1. You should instead use:
while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt
When you run the for
loop, bash will apply wordsplitting to what it reads, meaning that a strange blue cloud
will be read as a
, strange
, blue
and cloud
:
$ cat files
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt
Compare to:
$ while IFS= read -r file; do echo "$file"; done < files
a strange blue cloud.txt
Or even, if you insist on the UUoC:
$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt
So, the while
loop will read over its input and use the read
to assign each line to a variable. The IFS=
sets the input field separator to NULL*, and the -r
option of read
stops it from interpreting backslash escapes (so that \t
is treated as slash + t
and not as a tab). The --
after the mv
means "treat everything after the -- as an argument and not an option", which lets you deal with file names starting with -
correctly.
* This isn't necessary here, strictly speaking, the only benefit in this scenario is that keeps read
from removing any leading or trailing whitespace, but it is a good habit to get into for when you need to deal with filenames containing newline characters, or in general, when you need to be able to deal with arbitrary file names.
Best Answer
Yes, you would have to unset those options (with
shopt -u nullglob dotglob
) afterwards if you wanted the default globbing behaviour back in the current shell.You could just do
That would still generate an error without
nullglob
set if one of the patterns didn't match anything, obviously, but would work without having to set either option. It would probably also say something about failing to rename.
since it's a directory, but that too isn't stopping it from moving the files.A better option may be to use
rsync
locally:and then delete
~/public
.