গ্লোবকে `ফাইন্ডে রূপান্তর করুন`


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()regexes করার globs রূপান্তর করতে পারেন, তবে দুটো একসাথে ব্যবহার .*জন্য *জুড়ে মিলে /


যদি এটি সত্য হয় তাহলে প্রতিস্থাপন somethingসঙ্গে echoএটা করতে কর্তব্য।
ওলে টাঞ্জ

1
@ ওলেট্যাঞ্জ এজন্যই আমি প্রস্তাব দিয়েছি printf- এটি echoহাজার বার কল করার চেয়ে দ্রুত হবে এবং আরও নমনীয়তার প্রস্তাব দেয়।
মিউ

4
আর্গুমেন্টগুলির মধ্য দিয়ে যেতে পারে এমন একটি সীমা রয়েছে exec, যা বাহ্যিক আদেশ যেমন যেমন প্রযোজ্য cat; তবে এই সীমাটি শেল বিল্টিন কমান্ড যেমন প্রযোজ্য নয় printf
স্টিফেন কিট

1
@ ওলেট্যাঞ্জ লাইনটি খুব বেশি দীর্ঘ নয় কারণ printfএটি একটি বিল্টিন এবং এটি শেলগুলি সম্ভবত যুক্তি সরবরাহ করার জন্য একই পদ্ধতি ব্যবহার করে যা তারা তর্কগুলি গণনার জন্য ব্যবহার করে forcatএকটি অন্তর্নির্মিত না।
মিউ

1
প্রযুক্তিগতভাবে এমন শাঁস রয়েছে mkshযেখানে printfবিল্টিন নেই এবং শেলগুলি ksh93যেখানে অন্তর্নির্মিত cat(বা হতে পারে)। আরও দেখুন zargsমধ্যে zshঅবলম্বন না করেও এটি প্রায় কাজ xargs
স্টাফেন চেজেলাস

9

find( মানক -name/ -pathপূর্বাভাসের জন্য) ওয়াইল্ডকার্ড নিদর্শনগুলি ঠিক গ্লোবগুলির মতো ব্যবহার করে (নোটটি যে {a,b}কোনও গ্লোব অপারেটর নয়; প্রসারণের পরে আপনি দুটি গ্লোব পাবেন)। প্রধান পার্থক্য হ'ল স্ল্যাশগুলি পরিচালনা করা (এবং ডট ফাইল এবং ডায়ারগুলিকে বিশেষভাবে বিবেচনা করা হচ্ছে না 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বর্তমান ডিরেক্টরিতে ডিরেক্টরিগুলি বাদ দিয়ে অন্য ডিরেক্টরিগুলির বিষয়বস্তু পড়তে পারে - এবং ফাইলগুলির তালিকা বাছাই করা হত না But তবে আমরা যদি সমস্যাটি বাদ দিয়ে দিই [A-Z]বা অবৈধ চরিত্রের সাথে */ এর ?সাথে আচরণ করা হয়) অনির্ধারিত, আপনি একই ফাইলগুলির তালিকা পাবেন)।

তবে যে কোনও ক্ষেত্রেই, যেমনটি @ মুরু দেখিয়েছেন , সিস্টেম কলের সীমাটি পরিপূর্ণ করার জন্য findএটি কেবলমাত্র ফাইলের তালিকাকে কয়েক রানে বিভক্ত করার জন্য অবলম্বন করার দরকার নেই execve()। কিছু শেল zsh( যেমন zargs) বা ksh93(সহ command -x) এমনকি এটির জন্য অন্তর্নির্মিত সমর্থন করে।

সহ zsh(যার গ্লোবগুলিতেও সমান -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)একটি উল্লিখিত glob অপারেটর বিপরীত {,.bak}, (.)উল্লিখিত glob কোয়ালিফায়ার এর সমতূল্য finds 'এর -type f, অ্যাড oNমতো বাছাই এড়িয়ে যেতে সেখানে find, Dডট ফাইল অন্তর্ভুক্ত করার জন্য (উল্লিখিত glob প্রযোজ্য নয়))


Glo 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 এর সমস্ত ডিরেক্টরি ছাঁটাই হয় foo*barএবং সমস্ত স্তর 2 quux[A-Z]বা quux[A-Z].bakএকগুলি ব্যতীত এবং তারপরে pic...3 স্তরের একটি নির্বাচন করুন (এবং সেই স্তরের সমস্ত ডিরেক্টরি ছাঁটাই)।


3

আপনার প্রয়োজনীয়তার সাথে মিলে যাবার জন্য আপনি একটি রেজেক্স লিখতে পারেন:

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

এমন কোনও সরঞ্জাম আছে যা মানুষের ত্রুটিগুলি এড়াতে এই রূপান্তরটি করে?
ওলে টেঞ্জ

না, তবে কেবলমাত্র পরিবর্তনগুলিই ..bak*[^/]*
হ'ল পালানো, forচ্ছিক

এমনকি আপনার রূপান্তরটিও ভুল। ? [^ /] এ পরিবর্তিত হয় না। এটি হ'ল মানব ত্রুটি ঠিক এড়াতে চাই।
ওলে টেঞ্জ

1
আমি মনে করি [0-9][0-9][0-9][0-9]?[0-9]{3,4}
এড্রেপ দিয়ে

1
@ ওলেট্যাঞ্জ দেখুন গ্লোব এক্সপ্রেশন থেকে রেজেক্স তৈরি করুন
wjandrea

0

আমার অন্য উত্তরের নোটটিতে সাধারণীকরণ করা , আপনার প্রশ্নের আরও সরাসরি উত্তর হিসাবে আপনি এই পসিক্স shস্ক্রিপ্টটি বিশ্বকে একটি findঅভিব্যক্তিতে রূপান্তর করতে ব্যবহার করতে পারেন :

#! /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(আপনার উদাহরণ যা ব্যবহার দুটি globs তাই উল্লিখিত glob বক্রবন্ধনী সম্প্রসারণ ):

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

(যে ডট ফাইল উপেক্ষা করে না বা ছাড়া ডট dirs .এবং ..এবং না সাজানোর না ফাইলের তালিকা)।

এটি কেবল বর্তমান ডিরেক্টরি সম্পর্কিত কোনও গ্লোবগুলির সাথে কাজ করে, কোনও .বা ..উপাদান নেই। কিছু চেষ্টা করে, আপনি এটি কোনও গ্লোব ছাড়িয়ে কোনও গ্লোব পর্যন্ত প্রসারিত করতে পারেন ... এটিকেও অনুকূলিত করা যেতে পারে যাতে এটি glob2find 'dir/*'কোনও dirপ্যাটার্নের মতো দেখাবে না ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.