bash: रिक्त स्थान के साथ फ़ाइलें ले जाना


10

जब मैं फ़ाइल नाम में रिक्त स्थान के साथ एक एकल फ़ाइल को स्थानांतरित करता हूं तो यह इस तरह से काम करता है:

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

अब मेरे पास फाइलों की एक सूची है जिसमें रिक्त स्थान हो सकते हैं और मैं उन्हें स्थानांतरित करना चाहता हूं। उदाहरण के लिए:

$ 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

पहला उदाहरण क्यों काम करता है, लेकिन दूसरा एक नहीं है? मैं इसे कैसे कारगर बना सकता हूं?

जवाबों:


20

कभी, कभी उपयोगfor foo in $(cat bar) । यह एक क्लासिक गलती है, जिसे आमतौर पर बैश पिटफॉल नंबर 1 के रूप में जाना जाता है। आपको इसके बजाय उपयोग करना चाहिए:

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

जब आप चलाने के forपाश, पार्टी यह क्या पढ़ता को wordsplitting लागू होगा, जिसका अर्थ है कि a strange blue cloudके रूप में पढ़ा जाएगा a, strange, blueऔर cloud:

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

से तुलना:

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

या यहां तक ​​कि, अगर आप UUoC पर जोर देते हैं :

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

तो, whileलूप इसके इनपुट पर पढ़ेगा और readप्रत्येक लाइन को एक वैरिएबल को असाइन करने के लिए उपयोग करेगा । IFS=सेट शून्य करने के लिए इनपुट क्षेत्र विभाजक * , और -rका विकल्प readबैकस्लैश पलायन की व्याख्या (ताकि से यह बंद हो जाता है \tस्लेश + के रूप में व्यवहार किया जाता है tनहीं एक टैब के रूप और)। --के बाद mvका अर्थ है "इलाज सब कुछ के बाद - एक तर्क और नहीं एक विकल्प के रूप" है, जो आप फ़ाइल नाम के साथ शुरू से निपटने की सुविधा देता है -सही ढंग से।


* यह यहाँ आवश्यक नहीं है, कड़ाई से बोल रहा है, इस परिदृश्य में एकमात्र लाभ यह है कि readकिसी भी अग्रणी या अनुगामी व्हाट्सएप को हटाने से रहता है, लेकिन जब आप नए वर्णों वाले फ़ाइल नाम के साथ सौदा करने की आवश्यकता होती है, तो यह एक अच्छी आदत है सामान्य तौर पर, जब आपको मनमाने फ़ाइल नामों से निपटने में सक्षम होना चाहिए।


1
ठीक है, मैं केवल "$ फ़ाइल" भाग से बचने के तरीकों की तलाश कर रहा था। कहीं और त्रुटि की उम्मीद नहीं थी।
MrJingles87

@ MrJingles87 मुझे पता है। यह किसी न किसी बिंदु पर सभी को मिलता है । यदि आप इसके बारे में नहीं जानते हैं तो यह बहुत ही अनपेक्षित है।
terdon

@JohnKugelman हाँ वास्तव में, अच्छी बात है। उत्तर संपादित, धन्यवाद।
terdon

IFS=फ़ाइल नामों में नई सूचियों के साथ मदद नहीं करेगा। यहाँ फ़ाइल का प्रारूप केवल नए नाम के साथ फ़ाइल नाम की अनुमति नहीं देता है। IFS=अंतरिक्ष या टैब में समाप्त होने वाले फ़ाइल नामों के लिए आवश्यक है (या पहले से समाहित newline $ IFS के अलावा अन्य वर्ण)।
स्टीफन चेज़लस

यही कारण है कि बैच ख़तरा के उत्पादन में पार्स करने का प्रयास के बारे में अधिक है lsया findआदेश प्रतिस्थापन के साथ जब वहाँ बेहतर यह करने के लिए और अधिक विश्वसनीय तरीके हैं। $(cat file)सही होने पर ठीक हो जाएगा। यह थोड़ी देर के लिए पढ़ने वाले लूप के साथ "इसे सही करना" जितना मुश्किल है, यह उस के लिए + $ (...) एक है। मेरा जवाब देखिए।
स्टीफन चेजलस

11

यही कारण है कि $(cat file_list.txt)जैसे POSIX गोले में bashसूची संदर्भ में विभाजित + ग्लोब ऑपरेटर है ( zshकेवल करता विभाजन भाग के रूप में आप उम्मीद थी)।

यह $IFS(डिफ़ॉल्ट रूप से, SPC , TAB और NL) के पात्रों पर विभाजित होता है और जब तक आप पूरी तरह से ग्लोबिंग को बंद नहीं करते तब तक ग्लोब करता है।

यहाँ, आप केवल newline पर विभाजन करना चाहते हैं और गोलाकार भाग नहीं चाहते हैं, इसलिए यह होना चाहिए:

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(उदाहरण के लिए संकेतों के मामले में आवश्यक)।

इसका नुकसान यह है कि फ़ाइल की पूरी सामग्री को मेमोरी में संग्रहीत किया जाना है (कई बार जैसे शेल के साथ 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

बैश / पॉमिक्स गोले में एक चर को भूलने के लिए भूलने के सुरक्षा निहितार्थों पर अनपेक्षित छोड़ने के लिए इसका क्या अर्थ है इसके बारे में अधिक पढ़ना


मुझे लगता है कि आपको स्वीकार करना चाहिए, हालांकि, यह $(cat file_list.txt)पूरी फ़ाइल को पुनरावृत्ति करने से पहले स्मृति में while read ...पढ़ता है , जबकि केवल एक बार में एक पंक्ति पढ़ता है। यह बड़ी फ़ाइलों के लिए एक समस्या हो सकती है।
चेपनर

@chepner। अच्छी बात। जोड़ा गया।
स्टीफन चेज़लस

1

स्क्रिप्ट लिखने के बजाय, आप पा सकते हैं ..

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

मामले के लिए जब फ़ाइल फ़ाइल में स्थित हैं तो आप निम्न कार्य कर सकते हैं ::

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

हालांकि यह सुनिश्चित नहीं है ..

सौभाग्य


3
यदि आप उपयोग कर रहे हैं find, तो आप findसब कुछ के लिए भी उपयोग कर सकते हैं। इसमें क्यों लाया xargsगया? find -type f -iname '*.txt' -exec mv {} /folder/to/destination/
terdon

xargs बस यहाँ खोजने के परिणामों में हेरफेर करता है: -मैं हर परिणाम को एक चर F के लिए नियत करता हूँ जबकि 0 और Print0 बस गारंटी देता है कि रिक्त स्थान वाली फाइलें बच नहीं रही हैं। और -0 परिणामों से बचने से xargs को रोकता है। यह सुनिश्चित नहीं है कि कैसे रिक्त स्थान के साथ व्यवहार करता है ..
नोएल एलेक्स मकुमुली

2
-execमनमाने ढंग से फाइलनामों के साथ पूरी तरह से व्यवहार करता है (उन लोगों के साथ जिनमें रिक्त स्थान, न्यूलाइन्स या कोई अन्य अजीबता शामिल है)। मुझे पता है कि कैसे xargsकाम करता है, धन्यवाद :) मैं सिर्फ एक अलग कमांड को कॉल करने की बात नहीं देखता जब findआप पहले से ही यह कर सकते हैं। इसके साथ कुछ भी गलत नहीं है, बस अक्षम है।
terdon

@terdonyou सही हैं .. -exec {} / पाथ / डेस्टिनेशन सुचारू रूप से काम करता है .. मैं अधिक अतिरिक्त चीजों के कारण xargs पसंद करता हूं जो मैं इसके साथ करने में सक्षम हूं।
नोएल एलेक्स मकुमुली

xargsक्लॉबरिंग स्टडिन (जो mvइसके संकेतों के लिए आवश्यक है) का दोष भी है । इसके अलावा @ टेर्डन के समाधान के साथ एक समस्या है। जीएनयू xargsऔर प्रक्रिया प्रतिस्थापन के साथ गोले के साथ, समाप्त किया जा सकता हैxargs -0IF -a <(find...) mv F /dest
स्टीफन चेज़लस

-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

फ़ाइल =zenity --title "Pick a Movie" --file-selection

"$ {FILE [0]}" में फ़ाइल के लिए

omxplayer "$ {FILE [0]}" किया

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

! / Bin / bash

फ़ाइल =zenity --title "Pick a Song" --file-selection

"$ {FILE [0]}" में फ़ाइल के लिए

नाटक "$ {FILE [0]}" किया

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

! / Bin / bash

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

"$ {DIR [0]} में DIR के लिए"

cd "$ {DIR [0]}" && ढूंढें। -Type f -name ' .ogg' -o -name ' .mp3' | sort --version-sort | डीआईआर पढ़ते समय; किया "$ DIR" खेलते हैं

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

! / Bin / bash

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

"$ {DIR [0]} में DIR के लिए"

cd "$ {DIR [0]}" && ढूंढें। -Type f -name ' .ogg' -o -name ' .mp3' | डीआईआर पढ़ते समय; किया "$ DIR" खेलते हैं


कृपया मार्कडाउन के बारे में पढ़ें और अपने उत्तर के लिए कुछ मेकअप लागू करें। और कृपया समझाएं कि यह प्रश्न का उत्तर क्यों देता है। और क्या नहीं है !!!

मुझे लगता है कि यह जवाब इस सवाल का नहीं है। लेकिन अगर ऐसा है, तो यह एक सरल समस्या के लिए बहुत सारे सॉफ़्टवेयर स्थापित करने के लिए बहुत दूर जाता है।
MrJingles87
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.