Nigdy, nigdy nie używajfor foo in $(cat bar)
. Jest to klasyczny błąd, powszechnie znany jako bash pitfall numer 1 . Zamiast tego powinieneś użyć:
while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt
Po uruchomieniu for
pętli, bash będzie stosować wordsplitting do tego, co czyta, co oznacza, że a strange blue cloud
zostanie odczytany jako a
, strange
, blue
i cloud
:
$ cat files
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt
Porównać do:
$ while IFS= read -r file; do echo "$file"; done < files
a strange blue cloud.txt
Lub nawet, jeśli nalegasz na UUoC :
$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt
while
Pętla odczyta zatem dane wejściowe i użyje read
do przypisania każdej linii do zmiennej. W IFS=
ustawia separator pola wejściowe NULL * i -r
opcji z read
wstrzymuje ich interpretacji ucieczki ukośnikowe (tak, że \t
są traktowane jako kreską + t
, a nie jako kartę). „ --
Po mv
” oznacza „traktuj wszystko po - jako argument, a nie jako opcję”, co pozwala ci -
poprawnie zajmować się nazwami plików zaczynającymi się od .
* Nie jest to tutaj konieczne, ściśle mówiąc, jedyną korzyścią w tym scenariuszu jest to, że nie read
usuwa żadnych początkowych lub końcowych białych znaków, ale dobrym nawykiem jest, aby się do niego stosować, gdy trzeba radzić sobie z nazwami plików zawierającymi znaki nowej linii lub ogólnie, gdy trzeba umieć radzić sobie z dowolnymi nazwami plików.
IFS=
nie pomoże z nowymi liniami w nazwach plików. Format pliku tutaj po prostu nie pozwala na nazwy plików z nowymi liniami.IFS=
jest potrzebny dla nazw plików rozpoczynających się spacją lub tabulatorem (lub dowolnymi znakami innymi niż nowy znak $ IFS zawarty wcześniej).ls
lubfind
o zamianie poleceń, gdy istnieją lepsze, bardziej niezawodne sposoby wykonania tego.$(cat file)
byłoby dobrze, jeśli zrobiono to dobrze. Trudno jest „zrobić to dobrze” za pomocą pętli odczytu na chwilę, tak jak w przypadku pętli za + $ (...). Zobacz moją odpowiedź.To, że
$(cat file_list.txt)
w powłokach POSIX, podobnie jakbash
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ć:
Ma to również tę zaletę (nad
while read
pętlą), że odrzuca puste linie, zachowuje końcową linię niezakończoną i zachowujemv
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
izsh
).Z niektórych muszli (
ksh
,zsh
oraz w mniejszym stopniubash
), można zoptymalizować go$(<file_list.txt)
zamiast$(cat file_list.txt)
.Aby wykonać ekwiwalent za pomocą
while read
pętli, potrzebujesz:Lub z
bash
:Lub z
zsh
:Lub z GNU
mv
izsh
:Lub z GNU
mv
i GNUxargs
i ksh / zsh / bash: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
źródło
$(cat file_list.txt)
odczytuje cały plik do pamięci przed iteracją, podczas gdywhile read ...
odczytuje tylko jedną linię na raz. Może to stanowić problem w przypadku dużych plików.Zamiast pisać skrypt, możesz użyć find ..
w przypadku, gdy pliki znajdują się w pliku, możesz wykonać następujące czynności:
drugi nie jestem pewien ...
Powodzenia
źródło
find
, równie dobrze możesz użyćfind
do wszystkiego. Po coxargs
w to wnosić ?find -type f -iname '*.txt' -exec mv {} /folder/to/destination/
.-exec
zachowuje się doskonale w przypadku dowolnych nazw plików (w tym zawierających spacje, znaki nowej linii lub dowolne inne dziwactwa). Wiem, jakxargs
działa, dziękuję :) Po prostu nie widzę sensu wywoływania osobnego polecenia, kiedyfind
już mogę to zrobić dla ciebie. Nie ma w tym nic złego, po prostu nieefektywny.xargs
ma także wadę stobowania clobberinga (któramv
wymaga jego podpowiedzi). Również problem z rozwiązaniem @ terdon. Z GNUxargs
i powłokami z substytucją procesu można złagodzić za pomocąxargs -0IF -a <(find...) mv F /dest
#! / bin / bash
PLIK =
zenity --title "Pick a Movie" --file-selection
dla PLIKU w „$ {FILE [0]}”
wykonaj omxplayer „$ {FILE [0]}” gotowe
! / bin / bash
PLIK =
zenity --title "Pick a Song" --file-selection
dla PLIKU w „$ {FILE [0]}”
zagraj „$ {FILE [0]}” gotowe
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
dla DIR w „$ {DIR [0]}”
wykonaj cd „$ {DIR [0]}” i znajdź. -type f -name ' .ogg' -o -name ' .mp3' | sort --version-sort | podczas czytania DIR; zagraj „$ DIR” gotowe
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
dla DIR w „$ {DIR [0]}”
wykonaj cd „$ {DIR [0]}” i znajdź. -type f -name ' .ogg' -o -name ' .mp3' | podczas czytania DIR; zagraj „$ DIR” gotowe
źródło