ग्लॉब को `find` में बदलें


11

मेरे पास बार-बार यह समस्या है: मेरे पास एक ग्लोब है, जो बिल्कुल सही फाइलों से मेल खाता है, लेकिन इसका कारण है Command line too long। हर बार मैंने इसे कुछ संयोजन में बदल दिया है findऔर grepयह विशेष स्थिति के लिए काम करता है, लेकिन जो 100% समतुल्य नहीं है।

उदाहरण के लिए:

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

क्या ग्लब्स को findअभिव्यक्ति में बदलने का एक उपकरण है, जिसके बारे में मुझे जानकारी नहीं है? या फिर एक findग्लोब को एक ही ग्लोब से बिना सबडिर के मैच करने के लिए एक विकल्प है (जैसे foo/*.jpgकि मिलान करने की अनुमति नहीं है bar/foo/*.jpg)?


ब्रेस का विस्तार करें और आप के साथ परिणामी एक्सप्रेशन का उपयोग करने में सक्षम होना चाहिए -pathया -ipathfind . -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg'काम करना चाहिए - सिवाय इसके कि यह मेल खाएगा /fooz/blah/bar/quuxA/pic1234d.jpg। क्या यह एक समस्या हो सकती है?
मुरु

हाँ, यह एक समस्या होगी। यह 100% के बराबर होना चाहिए।
ओले तांगे

समस्या यह है कि हमारे पास कोई विचार नहीं है, वास्तव में क्या अंतर है। आपका पैटर्न बहुत ठीक है।
पीटर - मोनिका

मैंने आपके एक्सटेंशन पोस्ट को प्रश्न के उत्तर के रूप में जोड़ा। मुझे उम्मीद है कि यह इतना बुरा नहीं है।
पीटर - मोनिका

क्या आप यह नहीं कर सकते हैं echo <glob> | catकि मेरी जानकारी के अनुसार,
ईश बिल्ट

जवाबों:


15

यदि समस्या यह है कि आपको एक तर्क-सूची-एक-बहुत लंबी त्रुटि मिलती है, तो लूप, या शेल-इन-बिल्ट का उपयोग करें। जबकि command glob-that-matches-too-muchत्रुटि हो सकती है, for f in glob-that-matches-too-muchऐसा नहीं है, इसलिए आप बस कर सकते हैं:

for f in foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
do
    something "$f"
done

लूप कष्टदायी रूप से धीमा हो सकता है, लेकिन यह काम करना चाहिए।

या:

printf "%s\0" foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg |
  xargs -r0 something

( printfअधिकांश गोले में निर्मित किया जा रहा है, ऊपर execve()सिस्टम कॉल की सीमा के आसपास काम करता है )

$ cat /usr/share/**/* > /dev/null
zsh: argument list too long: cat
$ printf "%s\n" /usr/share/**/* | wc -l
165606

बैश के साथ भी काम करता है। मुझे यकीन नहीं है कि हालांकि यह कहां से प्रलेखित है।


विम glob2regpat()और पाइथन दोनों ही fnmatch.translate()ग्लब्स को रेग्जेस में बदल सकते हैं, लेकिन दोनों ही पूरे मैच के .*लिए इस्तेमाल करते हैं ।*/


यदि यह सच है, तो जगह somethingके साथ echoयह करना चाहिए।
ओले तांगे

1
@OleTange यही कारण है कि मैंने सुझाव दिया printf- यह echoहजारों बार कॉल करने से अधिक तेज़ होगा , और अधिक लचीलापन प्रदान करेगा।
मूरू

4
उन तर्कों पर एक सीमा होती है exec, जिनके माध्यम से पारित किया जा सकता है , जो बाहरी आदेशों पर लागू होता है जैसे cat; लेकिन उस सीमा शेल शेल कमांड पर लागू नहीं होती है जैसे कि printf
स्टीफन किट

1
@OleTange लाइन बहुत लंबी नहीं है क्योंकि printfयह एक बिलिन है, और गोले संभवतः तर्क देने के लिए उसी विधि का उपयोग करते हैं जिसका उपयोग वे तर्क वितर्क के लिए करते हैं forcatबिलियन नहीं है।
मूरू

1
तकनीकी तौर पर वहाँ की तरह गोले हैं mkshजहां printfऔर builtin नहीं है की तरह गोले ksh93जहां catहै (या हो सकता है) में निर्मित। यह भी देखने zargsके zshलिए चारों ओर काम करने के लिए सहारा लेने के बिना xargs
स्टीफन चेज़लस 10

9

find( -name/ -pathमानक विधेय के लिए) ग्लब्स की तरह वाइल्डकार्ड पैटर्न का उपयोग करता है (ध्यान दें कि {a,b}ग्लोब ऑपरेटर नहीं है; विस्तार के बाद, आपको दो ग्लब्स मिलते हैं)। मुख्य अंतर स्लैश (और डॉट फाइलें और dirs विशेष रूप से इलाज नहीं किया जा रहा है find) की हैंडलिंग है । *ग्लोब में कई निर्देशिकाओं का विस्तार नहीं होगा। */*/*सूचीबद्ध करने के लिए निर्देशिकाओं के 2 स्तरों तक कारण होगा। जोड़ना -path './*/*/*'किसी भी फाइल से मेल खाएगा जो कम से कम 3 स्तर गहरे हों और findकिसी भी निर्देशिका की सामग्री को किसी भी गहराई में सूचीबद्ध करने से नहीं रोकेंगे।

उस विशेष के लिए

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

ग्लब्स की जोड़ी, अनुवाद करना आसान है, आप गहराई 3 पर निर्देशिकाएं चाहते हैं, ताकि आप उपयोग कर सकें:

find . -mindepth 3 -maxdepth 3 \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

(या -depth 3कुछ findकार्यान्वयन के साथ )। या पॉसली:

find . -path './*/*/*' -prune \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

कौन सा गारंटी हैं उन है कि *और ?मिलान नहीं हो पाया /अक्षर।

( find, ग्लब्स के विपरीत, foo*barमौजूदा डाइरेक्टरी में अन्य के अलावा निर्देशिकाओं की सामग्री को पढ़ा जाएगा , और फ़ाइलों की सूची को क्रमबद्ध नहीं किया जाएगा। लेकिन अगर हम इस समस्या को छोड़ देते हैं कि अमान्य वर्णों के संबंध [A-Z]में */ के ?साथ या उसके व्यवहार से क्या मेल खाता है)। अनिर्दिष्ट, आपको फ़ाइलों की एक ही सूची मिलेगी)।

लेकिन किसी भी मामले में, जैसा कि @ मुरु ने दिखाया है , सिस्टम कॉल findकी सीमा के आसपास काम करने के लिए कई रनों में फ़ाइलों की सूची को विभाजित करने के लिए केवल इसका सहारा लेने की आवश्यकता नहीं है execve()। कुछ गोले जैसे zsh(के साथ zargs) या ksh93(के साथ command -x) भी उस के लिए बनाया समर्थन है।

साथ zsh(जिसका भी globs के बराबर है -type fऔर अधिकांश अन्य findविधेय), उदाहरण के लिए:

autoload zargs # if not already in ~/.zshrc
zargs ./foo*bar/quux[A-Z](|.bak)/pic[0-9][0-9][0-9][0-9]?.jpg(.) -- cmd

( (|.bak)इसके विपरीत एक ग्लोब ऑपरेटर है {,.bak}, (.)ग्लोब क्वालीफायर find's के बराबर है -type f, oNइसमें जैसे-जैसे छंटनी छोड़ना चाहते हैं find, Dडॉट-फाइल्स शामिल करना (इस ग्लोब पर लागू नहीं होता है))


Like findग्लोब जैसे डायरेक्टरी ट्री को क्रॉल करने के लिए आपको कुछ इस तरह की आवश्यकता होगी:

find . ! -name . \( \
  \( -path './*/*' -o -name 'foo*bar' -o -prune \) \
  -path './*/*/*' -prune -name 'pic[0-9][0-9][0-9][0-9]?.jpg' -exec cmd {} + -o \
  \( ! -path './*/*' -o -name 'quux[A-Z]' -o -name 'quux[A-Z].bak' -o -prune \) \)

यह स्तर 1 को छोड़कर सभी निर्देशिकाओं को prune कर रहा foo*barहै, और सभी स्तर 2 को छोड़कर quux[A-Z]या quux[A-Z].bakलोगों को छोड़कर , और फिर pic...स्तर 3 पर लोगों का चयन करें (और उस स्तर पर सभी निर्देशिकाओं को prune करें)।


3

आप अपनी आवश्यकताओं के मिलान के लिए एक रेक्सएक्स लिख सकते हैं:

find . -regextype egrep -regex './foo[^/]*bar/quux[A-Z](\.bak)?/pic[0-9][0-9][0-9][0-9][^/]?\.jpg'

क्या कोई ऐसा उपकरण है जो मानवीय त्रुटियों से बचने के लिए यह रूपांतरण करता है?
ओले तांगे

नहीं, लेकिन केवल बदलता है मैं से बचने के लिए थे ., के लिए वैकल्पिक मैच जोड़ने .bakऔर परिवर्तन *करने के लिए [^/]*पथ से मेल नहीं करने के लिए की तरह / foo / foo / बार आदि
sebasth

लेकिन यहां तक ​​कि आपका रूपांतरण गलत है। ? [^ /] में नहीं बदला गया है। यह ठीक उसी तरह की मानवीय भूल है जिससे मैं बचना चाहता हूं।
ओले तांगे

1
मैं egrep के साथ लगता है, तो आप को छोटा कर सकते [0-9][0-9][0-9][0-9]?करने के लिए[0-9]{3,4}
wjandrea


0

मेरे अन्य उत्तर पर नोट पर सामान्यीकरण , आपके प्रश्न के अधिक प्रत्यक्ष उत्तर के रूप में, आप shग्लॉस को findअभिव्यक्ति में बदलने के लिए इस POSIX स्क्रिप्ट का उपयोग कर सकते हैं :

#! /bin/sh -
glob=${1#./}
shift
n=$#
p='./*'

while true; do
  case $glob in
    (*/*)
      set -- "$@" \( ! -path "$p" -o -path "$p/*" -o -name "${glob%%/*}" -o -prune \)
      glob=${glob#*/} p=$p/*;;
    (*)
      set -- "$@" -path "$p" -prune -name "$glob"
      while [ "$n" -gt 0 ]; do
        set -- "$@" "$1"
        shift
        n=$((n - 1))
      done
      break;;
  esac
done
find . "$@"

एक मानक shग्लोब के साथ प्रयोग करने के लिए (इसलिए आपके उदाहरण के दो ग्लब्स जो ब्रेस विस्तार का उपयोग करते हैं ):

glob2find './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' \
  -type f -exec cmd {} +

(कि डॉट फ़ाइलों की अनदेखी नहीं करता है या छोड़कर डॉट-dirs .और ..करता है और तरह नहीं फ़ाइलों की सूची)।

यही कारण है कि एक ही है, वर्तमान निर्देशिका के सापेक्ष कोई साथ globs साथ काम करता है .या ..घटकों। कुछ प्रयास के साथ, आप इसे किसी भी ग्लोब से बढ़ा सकते हैं, एक ग्लोब से अधिक ... यह भी अनुकूलित किया जा सकता है ताकि यह वैसा ही glob2find 'dir/*'न दिखे dirजैसा कि एक पैटर्न के लिए था।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.