To, że $(cat file_list.txt)
w powłokach POSIX, podobnie jak bash
w kontekście listowym, jest operatorem podziału + glob ( zsh
wykonuje część dzieloną tylko tak, jak można się spodziewać).
Dzieli się na znaki $IFS
(domyślnie SPC , TAB i NL) i robi glob, chyba że całkowicie wyłączysz globowanie.
Tutaj chcesz podzielić tylko na nowy wiersz i nie chcesz części glob, więc powinno to być:
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
Ma to również tę zaletę (nad while read
pętlą), że odrzuca puste linie, zachowuje końcową linię niezakończoną i zachowuje mv
standardowe wejście (potrzebne na przykład w przypadku monitów).
Ma to jednak tę wadę, że cała zawartość pliku musi być przechowywana w pamięci (kilka razy w powłokach takich jak bash
i zsh
).
Z niektórych muszli ( ksh
, zsh
oraz w mniejszym stopniu bash
), można zoptymalizować go $(<file_list.txt)
zamiast $(cat file_list.txt)
.
Aby wykonać ekwiwalent za pomocą while read
pętli, potrzebujesz:
while IFS= read <&3 -r file || [ -n "$file" ]; do
{
[ -n "$file" ] || mv -- "$file" "new_place/$file"
} 3<&-
done 3< file_list.txt
Lub z bash
:
readarray -t files < file_list.txt &&
for file in "${files[@]}"
[ -n "$file" ] || mv -- "$file" "new_place/$file"
done
Lub z zsh
:
for file in ${(f)"$(<file_list.txt)"}
mv -- "$file" "new_place/$file"
done
Lub z GNU mv
i zsh
:
mv -t -- new_place ${(f)"$(<file_list.txt)"}
Lub z GNU mv
i GNU xargs
i ksh / zsh / bash:
xargs -rd '\n' -a <(grep . file_list.txt) mv -t -- new_place
Więcej informacji o tym, co to znaczy pozostawić rozszerzenia bez cudzysłowu na temat wpływu na bezpieczeństwo zapomnienia o cytowaniu zmiennej w powłokach bash / POSIX