That unquoted $(cat file_list.txt)
in POSIX shells like bash
in list context is the split+glob operator (zsh
only does the split part as you'd expect).
It splits on characters of $IFS
(by default, SPC, TAB and NL) and does glob unless you turn off globbing altogether.
Here, you want to split on newline only and don't want the glob part, so it should be:
IFS='
' # split on newline only
set -o noglob # disable globbing
for file in $(cat file_list.txt); do # split+glob
mv -- "$file" "new_place/$file"
done
That also has the advantage (over a while read
loop) to discard empty lines, preserve a trailing unterminated line, and preserve mv
's stdin (needed in case of prompts for instance).
It does have the disadvantage though that the full content of the file has to be stored in memory (several times with shells like bash
and zsh
).
With some shells (ksh
, zsh
and to a lesser extent bash
), you can optimise it with $(<file_list.txt)
instead of $(cat file_list.txt)
.
To do the equivalent with a while read
loop, you'd need:
while IFS= read <&3 -r file || [ -n "$file" ]; do
{
[ -n "$file" ] || mv -- "$file" "new_place/$file"
} 3<&-
done 3< file_list.txt
Or with bash
:
readarray -t files < file_list.txt &&
for file in "${files[@]}"
[ -n "$file" ] || mv -- "$file" "new_place/$file"
done
Or with zsh
:
for file in ${(f)"$(<file_list.txt)"}
mv -- "$file" "new_place/$file"
done
Or with GNU mv
and zsh
:
mv -t -- new_place ${(f)"$(<file_list.txt)"}
Or with GNU mv
and GNU xargs
and ksh/zsh/bash:
xargs -rd '\n' -a <(grep . file_list.txt) mv -t -- new_place
More reading about what it means to leave expansions unquoted at Security implications of forgetting to quote a variable in bash/POSIX shells
IFS=
won't help with newlines in file names. The format of the file here simply doesn't allow filenames with newlines.IFS=
is needed for file names beginning ending in space or tab (or whatever characters other than newline $IFS contained beforehand). – Stéphane Chazelas Sep 15 '17 at 12:34ls
orfind
with command substitutions when there are better, more reliable ways to do it.$(cat file)
would be alright when done right. It's about as difficult to "do it right" with a while read loop as it is with that a for + $(...) one. See my answer. – Stéphane Chazelas Sep 15 '17 at 12:37IFS=$'\n' for line in $(cat file); do . . .
which is why I linked to it. Perhaps a better link would be http://mywiki.wooledge.org/DontReadLinesWithFor . – terdon Sep 15 '17 at 12:42while
loop still has pitfalls. See in my answer how it has to be written to avoid them, which makes it not more legible than thefor
approach. – Stéphane Chazelas Sep 15 '17 at 12:47