একটি গ্রেপ রেজেক্স থেকে গ্রুপ ক্যাপচার করা হচ্ছে


380

আমি এই ছোট স্ক্রিপ্ট পেয়েছি sh(ম্যাক ওএসএক্স 10.6) ফাইলগুলির অ্যারের মাধ্যমে দেখতে। গুগল এই মুহুর্তে সহায়ক হওয়া বন্ধ করে দিয়েছে:

files="*.jpg"
for f in $files
    do
        echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
        name=$?
        echo $name
    done

এখনও অবধি (স্পষ্টতই, আপনার কাছে শেল গুরু) $nameকেবলমাত্র 0, 1 বা 2 ধরে রেখেছে, যদি grepপাওয়া যায় যে ফাইলের নামটি প্রদত্ত বিষয়টিটির সাথে মেলে কিনা on আমি যা চাই তা হ'ল প্যারেনগুলির মধ্যে যা আছে তা ক্যাপচার করা ([a-z]+)এবং এটি একটি চলকতে সঞ্চয় করা

আমি সম্ভব হলে কেবল ব্যবহারgrep করতে চাই । যদি তা না হয় তবে দয়া করে পাইথন বা পার্ল ইত্যাদির sedমতো কিছু বা এর মতো কিছু না - আমি শেল থেকে নতুন এবং এটি * নিক্স পিউরিস্ট কোণ থেকে আক্রমণ করতে চাই।

এছাড়াও, একটি দুর্দান্ত-শীতল বোনু হিসাবে, আমি কীভাবে শেলটিতে স্ট্রিং সংমিশ্রণ করতে পারি তা সম্পর্কে আমি আগ্রহী? আমি যে গোষ্ঠীটি ধরেছি তা হ'ল some নামে "কিছু নাম" স্ট্রিং ছিল এবং আমি তার শেষে ".jpg" স্ট্রিংটি যুক্ত করতে চেয়েছি, আমি cat $name '.jpg'কি পারি ?

আপনি যদি সময় পেয়ে থাকেন তবে কী চলছে তা দয়া করে ব্যাখ্যা করুন।


30
গ্রেপ কি সেডের চেয়ে সত্যিকারের বিশুদ্ধ ইউনিক্স?
মার্টিন ক্লেটনটন

3
আহ, এর মানে এই নয়। আমি কেবল আশা করছিলাম যে আমি এখানে বিশেষভাবে শেখার চেষ্টা করছি এমন একটি সরঞ্জাম ব্যবহার করে কোনও সমাধান পাওয়া যায়। যদি এটি ব্যবহার করে সমাধান করা সম্ভব না হয় grepতবে ব্যবহারের সমাধান করা sedযদি সম্ভব হয় তবে তা দুর্দান্ত sed
ইসহাক

2
আমার এই বিটিডব্লুতে একটি :) রাখা উচিত ছিল ...
মার্টিন ক্লেটনটন

Psh, আমার মস্তিষ্ক আজ খুব ভাজা।
ইসহাক

2
@ মার্টিনক্লেটন এটি একটি আকর্ষণীয় যুক্তি হতে চাই। আমি সত্যিই ভাবি যে সেড, (বা এডিট হিসাবে সুনির্দিষ্ট হতে হবে) ইউনিক্সটি আরও পুরনো হবে (এবং তাই বিশুদ্ধ? সম্ভবত?) ইউনিক্স কারণ গ্রেপ এর নামটি এড এক্সপ্রেশন জি (লোবলাল) / রি (গ্রুলার এক্সপ্রেশন) / পি (রিন্ট) থেকে প্রাপ্ত হয়েছে।
15-18 এ ffledgling

উত্তর:


499

আপনি যদি ব্যাশ ব্যবহার করছেন তবে আপনাকে ব্যবহার করতে হবে না grep:

files="*.jpg"
regex="[0-9]+_([a-z]+)_[0-9a-z]*"
for f in $files    # unquoted in order to allow the glob to expand
do
    if [[ $f =~ $regex ]]
    then
        name="${BASH_REMATCH[1]}"
        echo "${name}.jpg"    # concatenate strings
        name="${name}.jpg"    # same thing stored in a variable
    else
        echo "$f doesn't match" >&2 # this could get noisy if there are a lot of non-matching files
    fi
done

একটি পরিবর্তনশীল মধ্যে regex রাখা ভাল। আক্ষরিকভাবে অন্তর্ভুক্ত করা হলে কিছু নিদর্শন কাজ করবে না।

এটি =~যা ব্যাশের রেজেক্স ম্যাচ অপারেটর তা ব্যবহার করে। ম্যাচের ফলাফলগুলি বলা একটি অ্যারেতে সংরক্ষণ করা হয় $BASH_REMATCH। প্রথম ক্যাপচার গ্রুপটি সূচী 1, দ্বিতীয় (যদি থাকে) 2 তে সূচিত হয়, ইত্যাদি সূচক শূন্য পুরো ম্যাচ।

আপনার সচেতন হওয়া উচিত যে অ্যাঙ্করগুলি ছাড়াই এই রেজেেক্স (এবং যেটি ব্যবহার করছেন grep) নীচের উদাহরণগুলির সাথে আরও কয়েকটি মিলে যাবে, যা আপনি যা খুঁজছেন তা নাও হতে পারে:

123_abc_d4e5
xyz123_abc_d4e5
123_abc_d4e5.xyz
xyz123_abc_d4e5.xyz

দ্বিতীয় এবং চতুর্থ উদাহরণগুলি মুছে ফেলার জন্য আপনার রেজেেক্সটিকে এটির মতো করুন:

^[0-9]+_([a-z]+)_[0-9a-z]*

যা বলে যে স্ট্রিংটি অবশ্যই এক বা একাধিক অঙ্ক দিয়ে শুরু করা উচিত । ক্যারেট স্ট্রিংয়ের শুরুতে প্রতিনিধিত্ব করে। আপনি যদি রেগেক্সের শেষে ডলারের চিহ্নটি যুক্ত করেন তবে এটির মতো:

^[0-9]+_([a-z]+)_[0-9a-z]*$

এরপরে তৃতীয় উদাহরণটিও মুছে ফেলা হবে যেহেতু বিন্দুটি রেজেক্সের বর্ণগুলির মধ্যে নেই এবং ডলার চিহ্নটি স্ট্রিংয়ের শেষটিকে উপস্থাপন করে। নোট করুন যে চতুর্থ উদাহরণটিও এই ম্যাচটিকে ব্যর্থ করে।

আপনার যদি জিএনইউ থাকে grep(প্রায় 2.5 বা তার বেশি পরে, আমি মনে করি, যখন \Kঅপারেটর যুক্ত হয়েছিল):

name=$(echo "$f" | grep -Po '(?i)[0-9]+_\K[a-z]+(?=_[0-9a-z]*)').jpg

\Kঅপারেটর (পরিবর্তনশীল দৈর্ঘ্যের বর্ণন-পিছনে) ম্যাচ পূর্ববর্তী প্যাটার্ন সৃষ্টি করে কিন্তু ফলাফলে ম্যাচ অন্তর্ভুক্ত নয়। স্থির দৈর্ঘ্যের সমতুল্য (?<=)- সমাপ্তি বন্ধনীর আগে প্যাটার্নটি অন্তর্ভুক্ত করা হবে। আপনি ব্যবহার করতে হবে \Kযদি quantifiers বিভিন্ন লেন্থ এর স্ট্রিং মেলে পারে (যেমন +, *, {2,4})।

(?=)অপারেটর ম্যাচ নির্দিষ্ট অথবা পরিবর্তনশীল দৈর্ঘ্যের নিদর্শন এবং "চেহারা সম্মতি" বলা হয়। এটি ফলাফলের সাথে মিলে যাওয়া স্ট্রিংও অন্তর্ভুক্ত করে না।

ম্যাচ কেস-সংবেদনশীল করার জন্য, (?i)অপারেটরটি ব্যবহৃত হয়। এটি অনুসরণ করে এমন নিদর্শনগুলিকে প্রভাবিত করে যাতে এর অবস্থানটি উল্লেখযোগ্য।

ফাইলনামে অন্যান্য অক্ষর রয়েছে কিনা তার উপর নির্ভর করে রেজেক্সকে সামঞ্জস্য করা দরকার হতে পারে। আপনি নোট করবেন যে এই ক্ষেত্রে, আমি একই সাথে স্ট্রিংটি ক্যাপচার করা হয়েছে তার সাথে স্ট্রিংকে কাটানোর উদাহরণ দেখাব।


48
এই উত্তরে আমি নির্দিষ্ট রেখাকে উজ্জীবিত করতে চাই যা বলে যে "একটি চলকটিতে রেজেক্স স্থাপন করা ভাল। আক্ষরিকভাবে অন্তর্ভুক্ত করা থাকলে কিছু নিদর্শন কাজ করবে না।"
ব্র্যান্ডিন

5
@ ফ্রেঞ্চসো ফ্রেসাইনেলি: একটি উদাহরণ হল এমন একটি প্যাটার্ন যা সাদা স্থানকে অন্তর্ভুক্ত করে। এড়াতে অবাস্তব এবং আপনি উদ্ধৃতি ব্যবহার করতে পারবেন না কারণ এটি এটিকে একটি রেজেক্স থেকে একটি সাধারণ স্ট্রিংয়ে বাধ্য করে। এটি করার সঠিক উপায় হ'ল ভেরিয়েবল ব্যবহার করা। বিষয়গুলি অনেক সহজ করে দেওয়ার জন্য অ্যাসাইনমেন্টের সময় উদ্ধৃতিগুলি ব্যবহার করা যেতে পারে।
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

5
/Kঅপারেটর শিলা।
রাজ্জ ২

2
@ ব্র্যান্ডন: এটি কাজ করে। বাশের কোন সংস্করণ আপনি ব্যবহার করছেন? আপনি কী করছেন তা কাজ করে না তা আমাকে দেখান এবং সম্ভবত আমি কেন আপনাকে তা বলতে পারি।
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

2
@ এমডেললমো: আমার উত্তরে তথ্য অন্তর্ভুক্ত রয়েছে grep। এটি ওপি দ্বারাও গৃহীত হয়েছিল এবং প্রচুর পরিমাণে উন্নীত হয়েছিল। ডাউনভোটের জন্য ধন্যবাদ
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

145

খাঁটি দিয়ে এটি সম্ভবত সম্ভব নয় grep, কমপক্ষে সাধারণত না।

তবে যদি আপনার প্যাটার্নটি উপযুক্ত হয় তবে grepআপনি প্রথমে কোনও পরিচিত বিন্যাসে আপনার লাইনটি হ্রাস করতে পাইপলাইনের মধ্যে একাধিকবার ব্যবহার করতে সক্ষম হবেন এবং তারপরে আপনার পছন্দমতো কিছুটা বের করতে পারবেন। (যদিও সরঞ্জামগুলি এগুলিতে পছন্দ করে cutএবং sedএটি অনেক বেশি ভাল)।

ধরুন যে যুক্তিটির খাতিরে আপনার প্যাটার্নটি কিছুটা সহজ ছিল: [0-9]+_([a-z]+)_আপনি এটির মতো এটি বের করতে পারেন:

echo $name | grep -Ei '[0-9]+_[a-z]+_' | grep -oEi '[a-z]+'

প্রথমটি grepআপনার সামগ্রিক প্যাটার্নের সাথে মেলে না এমন কোনও লাইন সরিয়ে ফেলবে, দ্বিতীয়টি grep(যা --only-matchingনির্দিষ্ট করেছে ) নামের আলফা অংশটি প্রদর্শন করবে। এটি কেবলমাত্র কাজ করে কারণ প্যাটার্নটি উপযুক্ত: "আলফা অংশ" আপনার সন্ধানের জন্য যথেষ্ট নির্দিষ্ট।

(একপাশে: ব্যক্তিগতভাবে আমি ব্যবহার করতে চাই grep+ + cutঅর্জন করা কি তোমার পরে আছেন: echo $name | grep {pattern} | cut -d _ -f 2। এই পায় cutবিভেদক উপর বিভাজন দ্বারা ক্ষেত্রগুলির মধ্যে লাইন বিশ্লেষণ করতে _, এবং আয় মাত্র 2 ক্ষেত্র (ক্ষেত্র সংখ্যা 1 এ শুরু))।

ইউনিক্স দর্শনের একটি সরঞ্জাম রয়েছে যা একটি কাজ করে এবং এটি ভাল করে এবং অ-তুচ্ছ কাজগুলি সম্পাদনের জন্য তাদের একত্রিত করে, তাই আমি যুক্তি দিয়ে বলছি যে grep+ sedইত্যাদি জিনিসগুলি করার আরও এক অনন্য উপায় :-)


3
for f in $files; do name=প্রতিধ্বনি $ চ | গ্রেপ -ওইআই '[0-9] + _ ([এজেড] +) _ [0-9a-z] *' | কাট-ডি _ -ফ 2 ;আহা!
ইসহাক

2
আমি "দর্শনের" সাথে একমত নই আপনি যদি বাহ্যিক কমান্ড না কল করে শেল এর অন্তর্নির্মিত ক্ষমতাগুলি ব্যবহার করতে পারেন তবে আপনার স্ক্রিপ্টটি কার্য সম্পাদনে অনেক দ্রুত হবে। কিছু সরঞ্জাম রয়েছে যা কার্যক্রমে ওভারল্যাপ হয়। যেমন গ্রেপ এবং সেড এবং বিশ্রী aw এগুলির সবগুলি স্ট্রিং ম্যানিপুলেশনগুলি করে, তবে বিশ্রী তাদের সবার উপরে উঠে যায় কারণ এটি আরও অনেক কিছু করতে পারে। ব্যবহারিকভাবে, উপরের ডাবল গ্রেপস বা গ্রেপ + সেডের মতো সমস্ত কমান্ডের শৃঙ্খলাগুলি একটি অজক প্রক্রিয়া দিয়ে তাদের সংক্ষিপ্ত করা যেতে পারে।
ghostdog74

7
@ ঘোস্টডোগ 74৪: এখানে কোনও যুক্তি নেই যে প্রচুর ক্ষুদ্র অপারেশনগুলিকে এক সাথে শৃঙ্খলাবদ্ধ করা সমস্ত জায়গায় এক জায়গায় করার চেয়ে সাধারণত কম দক্ষ, তবে আমি আমার এই দৃ by়তার সাথে দাঁড়িয়েছি যে ইউনিক্স দর্শন এক সাথে কাজ করার প্রচুর সরঞ্জাম tools উদাহরণস্বরূপ, টার কেবল ফাইলগুলি সংরক্ষণাগার দেয়, এটি সংকোচিত হয় না এবং এটি ডিফল্টরূপে স্টকআউটকে আউটপুট দেয় বলে আপনি এটি নেটকাট দিয়ে নেটওয়ার্ক জুড়ে পাইপ করতে পারেন, বা এটি বিজিপ 2 দিয়ে সংকুচিত করতে পারেন ইত্যাদি যা আমার মনে কনভেনশন এবং সাধারণকে শক্তিশালী করে ইউনিক্স সরঞ্জামগুলি পাইপগুলিতে একসাথে কাজ করতে সক্ষম হওয়া উচিত os
রবএম

কাট অসাধারণ - টিপ জন্য ধন্যবাদ! সরঞ্জাম বনাম দক্ষতার যুক্তি হিসাবে, আমি চেইনিং সরঞ্জামগুলির সরলতা পছন্দ করি।
ইথার_জয়


96

আমি বুঝতে পারি যে এর জন্য একটি উত্তর ইতিমধ্যে গৃহীত হয়েছিল, তবে "কঠোরভাবে * নিক্স পিউরিস্ট অ্যাঙ্গেল" থেকে মনে হয় যে কাজের জন্য সঠিক সরঞ্জামটি pcregrep, যা এখনও উল্লেখ করা হয়নি বলে মনে হয়। লাইনগুলি পরিবর্তন করার চেষ্টা করুন:

    echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
    name=$?

পরবর্তী:

    name=$(echo $f | pcregrep -o1 -Ei '[0-9]+_([a-z]+)_[0-9a-z]*')

কেবল ক্যাপচারিং গ্রুপ 1 এর সামগ্রীগুলি পেতে।

pcregrepটুল একই সিনট্যাক্স আপনি ইতিমধ্যে সঙ্গে ব্যবহার করেছি সব সদ্ব্যবহার grepকিন্তু কার্যকরী কার্যকারিতা আপনার যা দরকার যে।

প্যারামিটারটি খালি থাকলে সংস্করণটির -oমতোই কাজ করে grepতবে এটিতে একটি সংখ্যাসূচক প্যারামিটারও গ্রহণ করা হয় pcregrepযা নির্দেশ করে যে আপনি কোন ক্যাপচারিং গ্রুপটি প্রদর্শন করতে চান।

এই সমাধানের সাথে স্ক্রিপ্টে একটি সর্বনিম্ন পরিবর্তন প্রয়োজন। আপনি কেবল একটির সাথে একটি মডুলার ইউটিলিটি অন্যটির সাথে প্রতিস্থাপন করুন এবং পরামিতিগুলিকে টুইঙ্ক করুন।

আকর্ষণীয় দ্রষ্টব্য: আপনি একাধিক ক্যাপচার গ্রুপগুলি যেভাবে লাইনে প্রদর্শিত হবে সেগুলি ফেরত দিতে আপনি একাধিক-যুক্তি ব্যবহার করতে পারেন।


3
pcregrepMac OS X
ডিপিটি

4
আমার "-o1" তে "অজানা বিকল্প বর্ণ '1" এর পরে অঙ্কটি pcregrepবুঝতে হবে বলে মনে হয় না -oAlso এছাড়াও দেখার সময় সেই কার্যকারিতার কোনও উল্লেখ নেইpcregrep --help
পিটার হারডেনবার্গ

1
@ ডাব্লুএফএফ দুঃখিত, অনুমান করুন যে আমার তথ্যটি আমার মন্তব্যে অন্তর্ভুক্ত করা উচিত ছিল। আমি CentOS 6.5 উপর আছি এবং pcregrep সংস্করণ দৃশ্যত খুব পুরানো হল: 7.8 2008-09-05
পিটার হারডেনবার্গ

2
হ্যাঁ, খুব সাহায্য করুন, উদাহরণস্বরূপecho 'r123456 foo 2016-03-17' | pcregrep -o1 'r([0-9]+)' 123456
zhuguowei

5
pcregrep8.41 ( apt-get install pcregrepচালু থাকা Ubuntu 16.03) -Eiস্যুইচটি চিনতে পারে না । যদিও এটি ছাড়াই নিখুঁতভাবে কাজ করে। উপরে মনিওসপেটেল উল্লিখিত হিসাবে ম্যাকোস-এ (এছাড়াও 8.41) দিয়ে pcregrepইনস্টল করা আছে homebrew, কমপক্ষে হাই সিয়েরায় -Eস্যুইচটিও স্বীকৃত নয়।
ভিল

27

আমি বিশ্বাস করি শুধু গ্রেপেই সম্ভব নয়

সেড জন্য:

name=`echo $f | sed -E 's/([0-9]+_([a-z]+)_[0-9a-z]*)|.*/\2/'`

আমি বোনাসে ছুরিকাঘাত করব যদিও:

echo "$name.jpg"

2
দুর্ভাগ্যক্রমে, sedসমাধানটি কাজ করে না। এটি কেবল আমার ডিরেক্টরিতে সমস্ত কিছু প্রিন্ট করে।
আইজাক

আপডেট হয়েছে, কোনও মিল না থাকলে একটি ফাঁকা লাইন আউটপুট দেবে, সুতরাং
এটির

এটি এখন কেবল ফাঁকা লাইন আউটপুট দেয়!
আইজাক

এই সেড একটি সমস্যা আছে। প্রথম বন্ধনীর প্রথম দলটি সমস্ত কিছুকে ঘিরে রেখেছে। অবশ্যই \ 2 এর কিছুই থাকবে না।
ghostdog74

এটি কিছু সাধারণ পরীক্ষার ক্ষেত্রে কাজ করেছে ... \ 2 অভ্যন্তরীণ দল পায়
কোব্বল

16

এটি এমন একটি সমাধান যা গাওক ব্যবহার করে। এটি এমন কিছু যা আমার প্রায়শই ব্যবহার করা দরকার বলে মনে হয় তাই এর জন্য একটি ফাংশন তৈরি করেছি

function regex1 { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'1'}']}'; }

ব্যবহার করতে

$ echo 'hello world' | regex1 'hello\s(.*)'
world

দুর্দান্ত ধারণা, তবে রেজিপ্লেক্সে ফাঁকা জায়গাগুলির সাথে কাজ করছে বলে মনে হচ্ছে না - তাদের সাথে প্রতিস্থাপন করা দরকার \s। আপনি কি এটি ঠিক করতে জানেন?
অ্যাডাম রাইজকোভস্কি

4

আপনার জন্য একটি পরামর্শ - আপনি শেষের আন্ডারস্কোর থেকে নামের অংশটি অপসারণ করতে এবং একইভাবে শুরুতে প্যারামিটার সম্প্রসারণ ব্যবহার করতে পারেন:

f=001_abc_0za.jpg
work=${f%_*}
name=${work#*_}

তারপরে nameমান হবে abc

অ্যাপল বিকাশকারী ডক্স দেখুন , 'প্যারামিটার সম্প্রসারণ' এর জন্য অনুসন্ধান করুন forward


এটি ([এজেড] +) এর জন্য পরীক্ষা করবে না।
ghostdog74

@ লিভিলেভিস - এটি সত্য, তবে ওপি-র মতামত অনুসারে এটি যা প্রয়োজন ছিল তা করে।
মার্টিন ক্লেটন

2

আপনার যদি বাশ থাকে তবে আপনি বর্ধিত গ্লোববিং ব্যবহার করতে পারেন

shopt -s extglob
shopt -s nullglob
shopt -s nocaseglob
for file in +([0-9])_+([a-z])_+([a-z0-9]).jpg
do
   IFS="_"
   set -- $file
   echo "This is your captured output : $2"
done

অথবা

ls +([0-9])_+([a-z])_+([a-z0-9]).jpg | while read file
do
   IFS="_"
   set -- $file
   echo "This is your captured output : $2"
done

এটি আকর্ষণীয় দেখায়। আপনি সম্ভবত এটি একটি সামান্য ব্যাখ্যা সংযোজন করতে পারেন? অথবা, আপনি যদি এত ঝুঁকে থাকেন তবে একটি বিশেষভাবে অন্তর্দৃষ্টিপূর্ণ সংস্থার সাথে লিঙ্ক করুন যা এটি ব্যাখ্যা করে? ধন্যবাদ!
আইজাক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.