2008-10-19 42 views
263

कहें कि मैं उन फ़ाइलों और फ़ोल्डरों को छोड़कर किसी निर्देशिका की सामग्री की प्रतिलिपि बनाना चाहता हूं जिनके नाम 'संगीत' शब्द शामिल हैं।यूनिक्स/लिनक्स खोल में पैटर्न मिलान करते समय मैं उलटा या नकारात्मक वाइल्डकार्ड का उपयोग कैसे कर सकता हूं?

cp [exclude-matches] *Music* /target_directory 

इसे पूरा करने के लिए [बहिष्कार-मिलान] के स्थान पर क्या जाना चाहिए?

उत्तर

304

बैश में आप इसे इस तरह, extglob विकल्प को सक्षम करने (सीपी के लिए ls की जगह और लक्ष्य निर्देशिका जोड़ने के लिए, निश्चित रूप से) द्वारा कर सकते हैं

~/foobar> shopt extglob 
extglob   off 
~/foobar> ls 
abar afoo bbar bfoo 
~/foobar> ls !(b*) 
-bash: !: event not found 
~/foobar> shopt -s extglob #Enables extglob 
~/foobar> ls !(b*) 
abar afoo 
~/foobar> ls !(a*) 
bbar bfoo 
~/foobar> ls !(*foo) 
abar bbar 

आप बाद में

shopt -u extglob 
+8

मुझे यह सुविधा पसंद है: 'ls/dir/* /! (Base *)' –

+6

आप सबकुछ (*) कैसे शामिल करते हैं और बहिष्कृत भी करते हैं! (बी *)? –

+2

'foo' को छोड़कर,' f' से शुरू होने वाली सभी चीज़ों से आप कैसे मिलान करेंगे? – Noldorin

4

इसके लिए एक समाधान मिल सकता है।

$ mkdir foo bar 
$ touch foo/a.txt foo/Music.txt 
$ find foo -type f ! -name '*Music*' -exec cp {} bar \; 
$ ls bar 
a.txt 

ढूंढने के लिए कुछ विकल्प हैं, आप जो भी शामिल करते हैं और बहिष्कृत करते हैं उस पर आप बहुत विशिष्ट हो सकते हैं।

संपादित करें: टिप्पणियों में एडम ने नोट किया कि यह रिकर्सिव है। विकल्प ढूंढें दिमाग और maxdepth इसे नियंत्रित करने में उपयोगी हो सकता है।

+0

यह एक रिकर्सिव कॉपी करता है, जो अलग-अलग व्यवहार है। यह प्रत्येक फ़ाइल के लिए एक नई प्रक्रिया भी पैदा करता है, जो बड़ी संख्या में फाइलों के लिए बहुत अक्षम हो सकता है। –

+0

एक प्रक्रिया को बढ़ाने की लागत सभी आईओ की तुलना में लगभग शून्य है जो प्रत्येक फ़ाइल उत्पन्न करता है। तो मैं कहूंगा कि यह कभी-कभी उपयोग के लिए पर्याप्त है। – dland

+0

प्रक्रिया के लिए कुछ कामकाज: http://stackoverflow.com/questions/186099/how-do-you-handle-the-too-many-files-problem-when-working-in-bash –

18
बैश में नहीं

(है कि मैं के बारे में पता है), लेकिन:

cp `ls | grep -v Music` /target_directory 

मैं जानता हूँ कि यह वास्तव में आप क्या देख रहे थे नहीं है, लेकिन यह अपने उदाहरण का समाधान होगा।

+0

डिफ़ॉल्ट एलएस प्रति पंक्ति एकाधिक फाइलें रखेगी, जो शायद सही परिणाम नहीं दे रहे हैं। –

+8

केवल तभी जब टर्मिनल टर्मिनल होता है। जब एक पाइपलाइन में उपयोग किया जाता है, तो एल प्रति पंक्ति एक फ़ाइल नाम प्रिंट करता है। टर्मिनल पर आउटपुट करते समय –

+0

एलएस केवल प्रति पंक्ति एकाधिक फाइलें रखता है। इसे स्वयं आज़माएं - "ls | less" में प्रति पंक्ति एकाधिक फाइलें नहीं होंगी। – SpoonMeiser

5

तुम भी एक बहुत सरल for पाश का उपयोग कर सकते हैं:

for f in `find . -not -name "*Music*"` 
do 
    cp $f /target/dir 
done 
+1

यह एक रिकर्सिव खोज करता है, जो ओपी चाहता है उससे अलग व्यवहार है। –

+1

गैर-पुनरावर्ती के लिए '-maxdepth 1' का उपयोग करें? – avtomaton

+0

मैंने शैल विकल्पों को सक्षम/अक्षम किए बिना यह सबसे साफ समाधान पाया। ओपी द्वारा परिणाम की आवश्यकता के लिए इस पोस्ट में -मैक्सडेप विकल्प की सिफारिश की जाएगी, लेकिन यह सब उस पर निर्भर करता है जो आप पूरा करने की कोशिश कर रहे हैं। –

9

साथ extglob को निष्क्रिय आप avo चाहते हैं कर सकते हैं exec कमांड का उपयोग करने की याद लागत आईडी, मुझे विश्वास है कि आप xargs के साथ बेहतर कर सकते हैं। मुझे लगता है कि निम्नलिखित extglob खोल विकल्प आपको कमांड लाइन में और अधिक शक्तिशाली पैटर्न मिलान देता

find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec 



find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/ 
186

करने के लिए एक अधिक कुशल विकल्प है।

आप इसे shopt -s extglob के साथ चालू करते हैं, और इसे shopt -u extglob के साथ बंद कर देते हैं।

अपने उदाहरण में, आप शुरू में करना होगा:

$ shopt -s extglob 
$ cp !(*Music*) /target_directory 

पूर्ण उपलब्ध ext समाप्त हो गया ग्लोब बिंग ऑपरेटरों (man bash से अंश) कर रहे हैं:

extglob खोल विकल्प हैं शॉपेट बिल्टिन का उपयोग करके सक्षम है, कई विस्तारित पैटर्न मिलान ऑपरेटरों को पहचाना जाता है। निम्नलिखित विवरण में, एक पेट टर्न-सूची एक या अधिक पैटर्न से अलग है।

  • : समग्र पैटर्न एक या अधिक निम्नलिखित उप-पैटर्न का उपयोग का गठन किया जा सकता है?(पैटर्न-सूची)
    मेल खाता शून्य या दिए गए पैटर्न में से एक घटना
  • * (पैटर्न-सूची)
    दिया पैटर्न के शून्य या अधिक घटनाओं मेल
  • + (पैटर्न-सूची)
    दिया पैटर्न
  • @ (पैटर्न-सूची)
    दिया पैटर्न में से एक मेल के एक या अधिक घटनाओं मेल
  • ! (पैटर्न-सूची)
    दिया पैटर्न में से एक के अलावा कुछ मेल

तो, उदाहरण के लिए, अगर आप या मौजूदा निर्देशिका में सभी फ़ाइलें .c नहीं हैं सूचीबद्ध करने के लिए करना चाहता था .h फ़ाइलें, आप क्या करेंगे:

$ ls -d !(*@(.c|.h)) 
बेशक

, सामान्य खोल globing काम करता है, इसलिए पिछले उदाहरण के रूप में भी लिखा जा सकता है:

$ ls -d !(*.[ch]) 
+0

यह वास्तव में अच्छा है! मैं इसे अधिक सामान्य बनाने के लिए थोड़ा सा सवाल संपादित करूंगा और आशा करता हूं कि आपका उत्तर कुछ और वोट दे सकता है। – user4812

+4

नाइटपिक: वे "नियमित अभिव्यक्ति" नहीं हैं, वे शैल पथनाम "पैटर्न" हैं –

+1

@glenn: आप बिल्कुल सही हैं। – tzot

0

इस संगीत जैसी चीजों को छोड़कर के लिए वास्तव में 'संगीत'

cp -a ^'Music' /target 

इस और उस को छोड़कर यह करना होगा? * या *? संगीत

cp -a ^\*?'complete' /target 
cp -a ^'complete'?\* /target 
+0

मैकोज़ पर 'सीपी' मैनुअल पेज में '-a' विकल्प है लेकिन यह कुछ पूरी तरह से अलग करता है। कौन सा मंच इसका समर्थन करता है? – tripleee

4

मेरे निजी पसंद ग्रेप उपयोग करने के लिए है और जबकि कमांड। यह एक को शक्तिशाली लेकिन पठनीय स्क्रिप्ट लिखने की अनुमति देता है जिससे आप यह सुनिश्चित कर सकें कि आप जो भी चाहते हैं उसे पूरा कर लें। इसके अलावा एक गूंज कमांड का उपयोग करके आप वास्तविक ऑपरेशन करने से पहले एक शुष्क रन कर सकते हैं। उदाहरण के लिए:

ls | grep -v "Music" | while read filename 
do 
echo $filename 
done 

उन फ़ाइलों को मुद्रित करेगा जिन्हें आप कॉपी करना समाप्त कर देंगे। यदि सूची सही है अगले कदम के रूप में इस बस कॉपी कमांड के साथ गूंज आदेश को बदलने के लिए है:

ls | grep -v "Music" | while read filename 
do 
cp "$filename" /target_directory 
done 
+1

यह तब तक काम करेगा जब तक आपके फ़ाइल नामों में कोई टैब, न्यूलाइन, पंक्ति में एक से अधिक स्थान या किसी बैकस्लैश नहीं हों। जबकि वे रोगजनक मामले हैं, संभावना के बारे में जागरूक होना अच्छा है। 'Bash' में आप' जबकि IFS = '' read -r फ़ाइल नाम 'का उपयोग कर सकते हैं, लेकिन फिर नईलाइन अभी भी एक समस्या है। आम तौर पर फाइलों की गणना करने के लिए 'ls' का उपयोग नहीं करना सबसे अच्छा है; 'ढूंढ' जैसे उपकरण बहुत बेहतर अनुकूल हैं। – Thedward

+0

किसी भी अतिरिक्त उपकरण के बिना: 'फ़ाइल में * के लिए; मामले में $ {file} करें (* संगीत *) ;; (*) सीपी "$ {file}"/target_directory; गूंज ;; esac; किया गया – Thedward

+0

http://mywiki.wooledge.org/ParsingLs कई अतिरिक्त कारणों को सूचीबद्ध करता है कि आपको इससे क्यों बचना चाहिए। – tripleee

2

निम्नलिखित, वर्तमान dir में सूचियों सभी *.txt फ़ाइलें काम करता है उन है कि एक नंबर के साथ शुरू छोड़कर।

यह bash, dash, zsh और अन्य सभी POSIX संगत शैल में काम करता है।

for FILE in /some/dir/*.txt; do # for each *.txt file 
    case "${FILE##*/}" in   # if file basename... 
     [0-9]*) continue ;;  # starts with digit: skip 
    esac 
    ## otherwise, do stuff with $FILE here 
done 
  1. पंक्ति एक पैटर्न /some/dir/*.txt में for पाश /some/dir जिसका नाम .txt के साथ समाप्त में सभी फाइलों से अधिक पुनरावृति करने के लिए कारण होगा।

  2. लाइन दो में एक केस स्टेटमेंट का उपयोग अवांछित फ़ाइलों को कम करने के लिए किया जाता है। - ${FILE##*/} अभिव्यक्ति फ़ाइल नाम से किसी भी अग्रणी डीआईआर नाम घटक से बाहर स्ट्रिप्स (यहां /some/dir/) ताकि पैटर केवल फ़ाइल के बेसनाम के खिलाफ मिल सकें। (यदि आप प्रत्यय के आधार पर केवल फाइलनामों को तबाह कर रहे हैं, तो आप इसे $FILE पर छोटा कर सकते हैं।)

  3. लाइन तीन में, case पैटर्न [0-9]*) लाइन मिलान सभी फाइलों को छोड़ दिया जाएगा (continue बयान for पाश की अगले चरण के लिए कूदता है)। - यदि आप चाहते हैं कि आप यहां कुछ और दिलचस्प कर सकें, उदा। [!a-z]* का उपयोग कर एक पत्र (ए-जेड) से शुरू नहीं होने वाली सभी फ़ाइलों को छोड़ना, या आप कई प्रकार के फ़ाइल नामों को छोड़ने के लिए कई पैटर्न का उपयोग कर सकते हैं उदा। [0-9]*|*.bak फ़ाइलों को .bak फ़ाइलों को छोड़ने के लिए, और फ़ाइलें जो संख्या से शुरू नहीं होती हैं।

+0

यह काम नहीं करता (डेबियन)। क्या आपने इसका परीक्षण किया? – lauhub

+0

दोह! एक बग था (मैं '*' के बजाय '* .txt' के खिलाफ मेल खाता था)। अब तय – zrajm

3

बैश में, shopt -s extglob के लिए एक विकल्प GLOBIGNORE variable है। यह वास्तव में बेहतर नहीं है, लेकिन मुझे याद रखना आसान लगता है।

एक उदाहरण हो सकता है कि क्या मूल पोस्टर चाहता था:

GLOBIGNORE="*techno*"; cp *Music* /only_good_music/ 

पूर्ण होने पर, unset GLOBIGNORE स्रोत निर्देशिका में rm *techno* करने में सक्षम हो।

2

एक चाल मैं यहाँ पर अभी तक नहीं देखा है कि उपयोग नहीं करता है extglob, find, या grep उन्हें प्रयोग comm सेट और "diff" के रूप में दो फ़ाइल सूचियों के इलाज के लिए है:

comm -23 <(ls) <(ls *Music*) 

commdiff से अधिक बेहतर है क्योंकि इसमें अतिरिक्त क्रूर नहीं है।

इस सेट 1, ls के सभी तत्वों, कि कर रहे हैं सेट 2, ls *Music* में नहीं भी देता है। ठीक से काम करने के लिए दोनों सेटों को क्रमबद्ध क्रम में होना आवश्यक है। ls और ग्लोब विस्तार के लिए कोई समस्या नहीं है, लेकिन यदि आप find जैसे कुछ उपयोग कर रहे हैं, तो sort को आमंत्रित करना सुनिश्चित करें।

comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort) 

संभावित रूप से उपयोगी।

+1

बहिष्करण के लाभों में से एक है निर्देशिका को पहले स्थान पर नहीं लेना है। यह समाधान उप-निर्देशिकाओं के * दो * ट्रैवर्सल करता है - एक बहिष्करण और बिना किसी के। –

+0

बहुत अच्छा बिंदु, @ मार्कस्टोसबर्ग। हालांकि, इस तकनीक का एक लाभकारी लाभ यह है कि आप वास्तविक फ़ाइल से बहिष्करण पढ़ सकते हैं, उदा। 'comm -23 <(ls) oute_these.list' –