bash: dosyaları boşlukla taşıma


10

Dosya adında boşlukları olan tek bir dosyayı taşıdığımda şöyle çalışır:

$ mv "file with spaces.txt" "new_place/file with spaces.txt"

Şimdi boşluk içerebilecek dosyaların bir listesi var ve bunları taşımak istiyorum. Örneğin:

$ echo "file with spaces.txt" > file_list.txt
$ for file in $(cat file_list.txt); do mv "$file" "new_place/$file"; done;

mv: cannot stat 'file': No such file or directory
mv: cannot stat 'with': No such file or directory
mv: cannot stat 'spaces.txt': No such file or directory

İlk örnek neden çalışıyor, ikincisi çalışmıyor? Nasıl çalıştırabilirim?

Yanıtlar:


20

Asla, asla kullanmayınfor foo in $(cat bar) . Bu, genellikle 1 numaralı bash tuzağı olarak bilinen klasik bir hatadır . Bunun yerine şunları kullanmalısınız:

while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt

Eğer çalıştırdığınızda fordöngü, bash okur ne wordsplitting geçerli olacak, yani a strange blue cloudolarak okunacaktır a, strange, blueve cloud:

$ cat files 
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt

Karşılaştırmak:

$ while IFS= read -r file; do echo "$file"; done < files 
a strange blue cloud.txt

Hatta UUoC konusunda ısrar ediyorsanız :

$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt

Böylece, whiledöngü girdisini okuyacak ve readher satırı bir değişkene atamak için. IFS=Setleri BOŞ için giriş alanı ayırıcı * ve -rseçeneği read(böylece ters eğik çizgi kaçar yorumlamaktan durur o \teğik çizgi + olarak kabul edilir tve bir sekme olarak). --Sonra mvaracı "tedavi şeyden sonra - bir argüman değil bir seçenek olarak" ile başlayan dosya adları ile anlaşma sağlayan, -doğru.


* Bu kesinlikle gerekli değildir, bu senaryoda tek fayda, readönde gelen veya sondaki boşlukları kaldırmayı engellemektir, ancak yeni satır karakterleri içeren dosya adları ile uğraşmanız gerektiğinde ele almak için iyi bir alışkanlıktır veya genel olarak, rastgele dosya adlarıyla başa çıkmanız gerektiğinde.


1
Tamam, sadece "$ file" bölümünden kaçmanın yollarını arıyordum. başka bir yerde hata beklemiyorduk.
MrJingles87

@ MrJingles87 Biliyorum. Bu herkesin bir noktada olmasını sağlar. Bunu bilmiyorsanız çok bilinçsizdir.
terdon

@JohnKugelman evet, iyi bir nokta. Cevap düzenlendi, teşekkürler.
terdon

IFS=dosya adlarındaki yeni satırlara yardımcı olmaz. Dosyanın biçimi, yeni satırlı dosya adlarına izin vermez. IFS=boşluk veya sekme ile biten dosya adları (veya önceden içerdiği newline $ IFS dışında herhangi bir karakter) için gereklidir.
Stéphane Chazelas

Bu toplu tuzak , daha iyi, daha güvenilir yollar olduğunda çıktıyı lsveya findkomut ikamelerini ayrıştırmaya çalışmakla ilgilidir . $(cat file)doğru yapıldığında iyi olur. Bir süre okuma döngüsü ile "doğru yapmak" yaklaşık bir + $ (...) için olduğu kadar zordur. Cevabımı gör.
Stéphane Chazelas

11

Bu $(cat file_list.txt)gibi POSIX kabuklarda bashliste kapsamında bölünmüş + glob operatörüdür ( zshyalnızca yapar bölünmüş beklediğiniz gibi parçası).

Karakterlerini böler $IFS(varsayılan olarak SPC , TAB ve NL) ve siz globbing'i tamamen kapatmazsanız glob yapar.

Burada, sadece yeni satıra bölünmek istiyorsunuz ve glob bölümünü istemiyorsunuz, bu yüzden olmalı:

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

Bunun da while readboş satırları atması, sonlandırılmamış bir satırı koruması ve mvstdin (örneğin bilgi istemleri için gerekli olması ) için avantajı vardır (bir döngü üzerinden ).

Dezavantajı, dosyanın tüm içeriğinin bellekte depolanması gerektiğidir ( bashve gibi kabuklarla birkaç kez zsh).

Bazı kabuklar ile ( ksh, zshve daha az bir ölçüde bash) kullanarak, ile optimize edilebilir $(<file_list.txt)yerine $(cat file_list.txt).

Eşdeğeri bir while readdöngü ile yapmak için ihtiyacınız olacak:

while IFS= read <&3 -r file || [ -n "$file" ]; do
  {
    [ -n "$file" ] || mv -- "$file" "new_place/$file"
  } 3<&-
done 3< file_list.txt

Veya bash:

readarray -t files < file_list.txt &&
for file in "${files[@]}"
  [ -n "$file" ] || mv -- "$file" "new_place/$file"
done

Veya zsh:

for file in ${(f)"$(<file_list.txt)"}
  mv -- "$file" "new_place/$file"
done

Veya GNU ile mvve zsh:

mv -t -- new_place ${(f)"$(<file_list.txt)"}

Veya GNU mvve GNU xargsve ksh / zsh / bash ile:

xargs -rd '\n' -a <(grep . file_list.txt) mv -t -- new_place

Güvenlik'te genişletmeleri boş bırakmanın ne anlama geldiğiyle ilgili daha fazla okuma , bash / POSIX mermilerinde bir değişkeni alıntılamayı unutmanın sonuçları


Yine de, $(cat file_list.txt)yinelemeden önce tüm dosyayı belleğe okuduğunu kabul etmelisiniz , oysa while read ...bir seferde yalnızca bir satır okur. Bu büyük dosyalar için bir sorun olabilir.
chepner

@chepner. İyi bir nokta. Katma.
Stéphane Chazelas

1

Senaryo yazmak yerine find komutunu kullanabilirsiniz.

 find -type f -iname \*.txt -print0 | xargs -IF -0 mv F /folder/to/destination/

dosyaların dosyada bulunduğu durumda aşağıdakileri yapabilirsiniz ::

cat file_with_spaces.txt | xargs -IF -0 mv F /folder/to/destination

ikincisi emin değilim ..

İyi şanslar


3
Eğer kullanıyorsanız find, findher şey için de kullanabilirsiniz . Neden getireyim xargs? find -type f -iname '*.txt' -exec mv {} /folder/to/destination/.
terdon

xargs burada bulmanın sonuçlarını basitçe manipüle eder: -I her sonucu F değişkenine atarken, 0 ve print0 boşluklu dosyaların kaçmamasını garanti eder. ve -0, xargs'ın sonuçlardan kaçmasını önler.
-exec'in

2
-execrastgele dosya adları (boşluklar, yeni satırlar veya başka bir tuhaflık içerenler dahil) ile mükemmel bir şekilde çalışır. Nasıl xargsçalıştığını biliyorum , teşekkürler :) findZaten sizin için yapabileceği zaman ayrı bir komut çağırmanın anlamını görmüyorum. Bunda yanlış bir şey yok, sadece verimsiz.
terdon

@terdonyou haklısın .. -exec {} / yol / hedef sorunsuz çalışıyor .. Ben onunla yapabileceğim daha fazla şey nedeniyle xargs tercih ederim.
Noel Alex Makumuli

xargsaynı zamanda ( mvistemleri için gereken) klozet stdin dezavantajı vardır . Ayrıca @ terdon'un çözümü ile ilgili bir sorun. GNU xargsve proses ikameli mermiler ile hafifletilebilirxargs -0IF -a <(find...) mv F /dest
Stéphane Chazelas

-1
FIRST INSTALL 

apt-get install chromium-browser

apt-get install omxplayer

apt-get install terminator

apt-get install nano (if not already installed)

apt-get install zenity (if not already installed)

THEN CREATE A BASH SCRIPT OF ALL OF THESE PERSONALLY WRITTEN SCRIPTS. 

MAKE SURE THAT EVERY SHELL SCRIPT IS A .sh FILE ENDING EACH ONE OF THESE IS SCRIPTED TO OPEN UP A DIRECTORY FOR YOU TO FIND AND PICK WHAT YOU WANT. 

IT RUNS A BACKGROUND TERMINAL & ALLOWS YOU TO CONTROL THE SONG OR MOVIE.  

MOVIE KEYS ARE AS FOLLOWS; p or space bar for pause, q for quit, - & + for sound up and down, left arrow and right arrow for skipping forward and back. 

MUSIC PLAYER REALLY ONLY HAS A SKIP SONG AND THAT IS CTRL+C AND IF THAT IS THE LAST SONG OR ONLY SONG THEN IT SHUTS DOWN AND THE TERMINAL GOES AWAY.


****INSTRUCTIONS TO MAKE THE SCRIPTS****
- OPEN UP A TERMINAL 

- CD TO THE DIRECTORY YOU WANT THE SCRIPTS TO BE
cd /home/pi/Desktop/

- OPEN UP THE NANO EDITOR WITH THE TITLE OF SHELL YOU WANT
sudo nano Movie_Player.sh

- INSIDE NANO, TYPE OR COPY/PAST (REMEMBER THAT IN TERMINAL YOU NEED TO CTRL+SHIFT+V) THE SCRITP

- SAVE THE DATA WITH CTRL+O
ctrl+o

- HIT ENTER TO SAVE AS THAT FILE NAME OR DELETE THE FILE NAME THEN TYPE NEW ONE JUST MAKE SURE IT ENDS IN .sh THEN HIT ENTER

- NEXT YOU NEED TO SUDO CHMOD IT WITH +X TO MAKE IT CLICKABLE AS A BASH
sudo chmod +x Movie_Player.sh

- FINALLY RUN IT TO TEST EITHER BY DOUBLE CLICKING IT AND CHOOSING "EXECUTE IN TERMINAL" OR BY ./ IT IN TERMINAL
./Movie_Player.sh

YOU ARE NOW GOOD TO PICK A MOVIE OR SELECT A SONG OR ALBUM AND ENJOY!  




**** ALL OF THESE SCRIPTS ACCOUNT FOR SPACES IN THE FILENAME 
SO YOU CAN ACCESS "TOM PETTY" OR "SIXTEEN STONE" WITHOUT NEEDING THE " _ " BETWEEN THE WORDS.




-------WATCH A MOVIE SCRIPT ------- (

#! / Bin / bash

DOSYA =zenity --title "Pick a Movie" --file-selection

"$ {FILE [0]}" içindeki FILE için

omxplayer "$ {FILE [0]}" bitti

--------LISTEN TO A SONG -------

! / Bin / bash

DOSYA =zenity --title "Pick a Song" --file-selection

"$ {FILE [0]}" içindeki FILE için

"$ {FILE [0]}" oynatıldı tamamlandı

--------LISTEN TO AN ALBUM ---------- SONGS IN ORDER

! / Bin / bash

DIR =zenity --title "Pick a Album" --file-selection --directory

"$ {DIR [0]}" içindeki DIR için

cd "$ {DIR [0]}" && bul. -tip f-adı ' .ogg' -o-adı ' .mp3' | sıralama - sürüm sıralama | DIR okunurken; oynamak "$ DIR" bitti yapılır

--------LISTEN TO AN ALBUM WITH SONGS IN RANDOM ORDER --------

! / Bin / bash

DIR =zenity --title "Pick a Album" --file-selection --directory

"$ {DIR [0]}" içindeki DIR için

cd "$ {DIR [0]}" && bul. -tip f-adı ' .ogg' -o-adı ' .mp3' | DIR okunurken; oynamak "$ DIR" bitti yapılır


Lütfen işaretlemeyi okuyun ve cevabınıza bir miktar makyaj uygulayın. Ve lütfen bunun neden soruyu cevapladığını açıklayın. VE GÖSTERMEYİN !!!

Bu cevabın bu soruya ait olmadığını hissediyorum. Ama eğer öyleyse, böyle basit bir sorun için çok fazla yazılım yüklemek çok uzaklara gidiyor.
MrJingles87
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.