Jangan pernah gunakanfor foo in $(cat bar)
. Ini adalah kesalahan klasik, umumnya dikenal sebagai bash pitfall nomor 1 . Anda sebaiknya menggunakan:
while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt
Ketika Anda menjalankan for
lingkaran, bash akan berlaku wordsplitting untuk apa yang dibaca, yang berarti bahwa a strange blue cloud
akan dibaca sebagai a
, strange
, blue
dan cloud
:
$ cat files
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt
Dibandingkan dengan:
$ while IFS= read -r file; do echo "$file"; done < files
a strange blue cloud.txt
Atau bahkan, jika Anda bersikeras UUoC :
$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt
Jadi, while
loop akan membaca inputnya dan menggunakan read
untuk menetapkan setiap baris ke variabel. The IFS=
set pemisah field input ke NULL * , dan -r
pilihan untuk read
berhenti dari menafsirkan lolos backslash (sehingga \t
diperlakukan sebagai slash + t
dan bukan sebagai tab). The --
setelah mv
berarti "memperlakukan semuanya setelah - sebagai argumen dan bukan pilihan", yang memungkinkan Anda berurusan dengan nama file dimulai dengan -
benar.
* Ini tidak perlu di sini, secara tegas, satu-satunya manfaat dalam skenario ini adalah menjaga read
agar tidak menghapus spasi putih depan atau belakang, tetapi merupakan kebiasaan yang baik untuk dilakukan ketika Anda harus berurusan dengan nama file yang mengandung karakter baris baru, atau secara umum, ketika Anda harus dapat menangani nama file yang sewenang-wenang.
IFS=
tidak akan membantu dengan baris baru dalam nama file. Format file di sini tidak memungkinkan nama file dengan baris baru.IFS=
diperlukan untuk nama file yang dimulai dengan spasi atau tab (atau karakter apa pun selain baris $ IFS yang terkandung sebelumnya).ls
ataufind
dengan substitusi perintah ketika ada yang lebih baik, cara yang lebih dapat diandalkan untuk melakukannya.$(cat file)
akan baik-baik saja jika dilakukan dengan benar. Ini tentang sama sulitnya untuk "melakukannya dengan benar" dengan loop baca sementara seperti halnya dengan $ + (...) satu. Lihat jawaban saya.Bahwa
$(cat file_list.txt)
dalam shell POSIX sepertibash
dalam konteks daftar adalah operator split + glob (zsh
hanya melakukan bagian split seperti yang Anda harapkan).Itu terbagi pada karakter
$IFS
(secara default, SPC , TAB dan NL) dan melakukan glob kecuali Anda mematikan globbing sama sekali.Di sini, Anda ingin membagi hanya pada baris baru dan tidak ingin bagian glob, jadi seharusnya:
Itu juga memiliki keuntungan (lebih dari satu
while read
loop) untuk membuang garis kosong, mempertahankan garis trailing yang tidak ditentukan, dan mempertahankanmv
stdin (diperlukan jika diminta misalnya).Itu memang memiliki kekurangan meskipun isi penuh file harus disimpan dalam memori (beberapa kali dengan kerang seperti
bash
danzsh
).Dengan beberapa shell (
ksh
,zsh
dan pada tingkat lebih rendahbash
), Anda dapat mengoptimalkannya dengan$(<file_list.txt)
alih - alih$(cat file_list.txt)
.Untuk melakukan hal yang sama dengan
while read
loop, Anda perlu:Atau dengan
bash
:Atau dengan
zsh
:Atau dengan GNU
mv
danzsh
:Atau dengan GNU
mv
dan GNUxargs
dan ksh / zsh / bash:Lebih banyak membaca tentang apa artinya membiarkan ekspansi tidak dikutip di Implikasi keamanan lupa mengutip variabel dalam bash / POSIX shells
sumber
$(cat file_list.txt)
membaca seluruh file ke dalam memori sebelum iterasi, sedangkanwhile read ...
hanya membaca satu baris pada satu waktu. Ini bisa menjadi masalah untuk file besar.Alih-alih menulis skrip, Anda dapat menggunakan find ..
untuk kasus ketika file berada dalam file maka Anda dapat melakukan hal berikut ::
yang kedua tidak yakin ..
Semoga berhasil
sumber
find
, Anda bisa menggunakanfind
semuanya. Mengapa membawanyaxargs
?find -type f -iname '*.txt' -exec mv {} /folder/to/destination/
.-exec
berperilaku sempurna dengan nama file yang berubah-ubah (termasuk yang mengandung spasi, baris baru atau keanehan lainnya). Saya tahu caraxargs
kerjanya, terima kasih :) Saya hanya tidak melihat titik memanggil perintah terpisah ketikafind
sudah bisa melakukannya untuk Anda. Tidak ada yang salah dengan itu, hanya saja tidak efisien.xargs
juga memiliki kekurangan stdin clobbering (yangmv
perlu untuk meminta nya). Juga masalah dengan solusi @ terdon. Dengan GNUxargs
dan cangkang dengan proses substitusi, dapat diringankan denganxargs -0IF -a <(find...) mv F /dest
#! / bin / bash
FILE =
zenity --title "Pick a Movie" --file-selection
untuk FILE dalam "$ {FILE [0]}"
lakukan omxplayer "$ {FILE [0]}" selesai
! / bin / bash
FILE =
zenity --title "Pick a Song" --file-selection
untuk FILE dalam "$ {FILE [0]}"
jangan mainkan "$ {FILE [0]}" selesai
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
untuk DIR dalam "$ {DIR [0]}"
lakukan cd "$ {DIR [0]}" && find. -type f -name ' .ogg' -o -name ' .mp3' | sort --version-sort | saat membaca DIR; jangan bermain "$ DIR" selesai dilakukan
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
untuk DIR dalam "$ {DIR [0]}"
lakukan cd "$ {DIR [0]}" && find. -type f -name ' .ogg' -o -name ' .mp3' | saat membaca DIR; jangan bermain "$ DIR" selesai dilakukan
sumber