Те, що $(cat file_list.txt)
в оболонках POSIX, як bash
у контексті списку, є оператор split + glob ( zsh
виконує лише розділену частину, як ви очікували).
Він розділяється на символи $IFS
(за замовчуванням, SPC , TAB та NL) і робить глобус, якщо ви зовсім не вимкнете глобалізацію.
Тут ви хочете розділити лише на новий рядок, а не хотіти глобальну частину, тож слід:
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
Це також має перевагу (над while read
циклом) для відміни порожніх рядків, збереження безперервної рядкової лінії та збереження mv
stdin (необхідний у разі запитів, наприклад).
Він має і недолік, але повний вміст файлу повинен зберігатися в пам'яті (кілька разів з оболонками як bash
і zsh
).
З деякими оболонок ( ksh
, zsh
і в меншій мірі bash
), ви можете оптимізувати його $(<file_list.txt)
замість $(cat file_list.txt)
.
Щоб зробити еквівалент із while read
циклом, вам знадобиться:
while IFS= read <&3 -r file || [ -n "$file" ]; do
{
[ -n "$file" ] || mv -- "$file" "new_place/$file"
} 3<&-
done 3< file_list.txt
Або з bash
:
readarray -t files < file_list.txt &&
for file in "${files[@]}"
[ -n "$file" ] || mv -- "$file" "new_place/$file"
done
Або з zsh
:
for file in ${(f)"$(<file_list.txt)"}
mv -- "$file" "new_place/$file"
done
Або з GNU mv
та zsh
:
mv -t -- new_place ${(f)"$(<file_list.txt)"}
Або з GNU mv
та GNU xargs
та ksh / zsh / bash:
xargs -rd '\n' -a <(grep . file_list.txt) mv -t -- new_place
Детальніше про те, що означає залишити розширення без котирування під впливом безпеки, забувши процитувати змінну в оболонках bash / POSIX