কীভাবে গ্রেপ-ইনভার্স-ম্যাচ করবেন এবং "আগে" এবং "পরে" লাইনগুলি বাদ দিন


26

নিম্নলিখিত এন্ট্রি সহ একটি পাঠ্য ফাইল বিবেচনা করুন:

aaa
bbb
ccc
ddd
eee
fff
ggg
hhh
iii

একটি প্যাটার্ন দেওয়া (যেমন fff), আউটপুট পেতে আমি উপরের ফাইলটি গ্রেপ করতে চাই:

all_lines except (pattern_matching_lines  U (B lines_before) U (A lines_after))

উদাহরণস্বরূপ, যদি B = 2এবং A = 1, প্যাটার্ন = সহ আউটপুট fffহওয়া উচিত:

aaa
bbb
ccc
hhh
iii

গ্রেপ বা অন্যান্য কমান্ড লাইন সরঞ্জামগুলির সাহায্যে আমি এটি কীভাবে করতে পারি?


দ্রষ্টব্য, যখন আমি চেষ্টা করি:

grep -v 'fff'  -A1 -B2 file.txt

আমি যা চাই তা পাই না। পরিবর্তে আমি পেতে:

aaa
bbb
ccc
ddd
eee
fff
--
--
fff
ggg
hhh
iii

উত্তর:


9

ডনের বেশিরভাগ ক্ষেত্রেই ভালো হতে পারে, কিন্তু ধরো যদি ফাইল সত্যিই বড়, এবং আপনি পাবেন না sedএকটি স্ক্রিপ্ট ফাইল বড় যে হ্যান্ডেল করতে (যা স্ক্রিপ্ট প্রায় 5000+ টি লাইন ঘটতে পারে) , এখানে এটা প্লেইন সাথে আছেন sed:

sed -ne:t -e"/\n.*$match/D" \
    -e'$!N;//D;/'"$match/{" \
            -e"s/\n/&/$A;t" \
            -e'$q;bt' -e\}  \
    -e's/\n/&/'"$B;tP"      \
    -e'$!bt' -e:P  -e'P;D'

এটি ইনপুটটিতে স্লাইডিং উইন্ডো যাকে বলা হয় তার একটি উদাহরণ । এটি কোনও কিছু মুদ্রণের চেষ্টা করার আগে - অ্যাকাউন্টের লাইনগুলির চেহারা-এগিয়ে বাফার তৈরি করে কাজ করে $B

এবং প্রকৃতপক্ষে, সম্ভবত আমার আমার পূর্ববর্তী বিষয়টি স্পষ্ট করা উচিত: এই সমাধান এবং ডনের উভয়ের জন্য প্রাথমিক পারফরম্যান্স সীমাবদ্ধতা সরাসরি বিরতির সাথে সম্পর্কিত হবে। এই সমাধান বৃহত্তর ব্যবধান সঙ্গে মন্থর করে দেবে মাপ , যেহেতু ডনের বড় ব্যবধান সঙ্গে মন্থর করে দেবে ফ্রিকোয়েন্সি । অন্য কথায়, এমনকি যদি ইনপুট ফাইলটি খুব বড় হয় তবে প্রকৃত বিরতি ঘটনা যদি এখনও খুব কম হয় তবে তার সমাধান সম্ভবত যাওয়ার উপায়। তবে, যদি অন্তর আকারটি তুলনামূলকভাবে পরিচালনাযোগ্য হয় এবং প্রায়শই ঘটে যাওয়ার সম্ভাবনা থাকে, তবে এটি আপনার বেছে নেওয়া উচিত সমাধান।

সুতরাং ওয়ার্কফ্লো এখানে:

  • যদি $matchকোনও ই- \nলাইন এর আগে প্যাটার্ন স্পেসে পাওয়া যায় তবে এর আগে প্রতিটি ইওলাইন sedপুনরাবৃত্তভাবে একাদশ করতে হবে। D\n
    • আমি $matchএর পূর্বে সম্পূর্ণরূপে প্যাটার্নের জায়গাটি সাফ করে দিচ্ছিলাম - তবে সহজেই ওভারল্যাপটি পরিচালনা করতে, একটি ল্যান্ডমার্ক রেখে আরও ভাল কাজ করবে বলে মনে হয়।
    • আমি এটি একবারে s/.*\n.*\($match\)/\1/পেতে এবং লুপটি ডজ করার চেষ্টা করার চেষ্টাও করেছি , তবে যখন $A/$Bবড় হয়, তখন Dএলেটের লুপটি যথেষ্ট দ্রুত প্রমাণিত হয়।
  • তারপর আমরা টেনে Nএকটি পূর্বে ইনপুটের EXT লাইন \newline বিভেদক এবং আবার চেষ্টা করুন Dএকটি elete /\n.*$match/আমাদের অতি সম্প্রতি যেটি ব্যবহার রেগুলার এক্সপ্রেশন W / বিষয়ে উল্লেখ করে আবার //
  • যদি প্যাটার্ন স্পেসের সাথে মেলে $matchতবে এটি কেবলমাত্র লাইনের শিরোনামে এটি করতে পারে $match- সমস্ত $Bইওরোফর লাইন সাফ হয়ে গেছে।
    • সুতরাং আমরা $After উপর লুপিং শুরু ।
    • এই s///লুপটির প্রতিটি রান আমরা প্যাটার্ন স্পেসের &নিজস্ব $Aই- \nলাইন চরিত্রটি নিজের জন্য বদলে নেওয়ার চেষ্টা করব এবং সফল হলে এটি tআমাদের শাখা করে দেবে - এবং আমাদের পুরো $Aঅবধি বাফার - পুরোপুরি স্ক্রিপ্টটি উপরের দিক থেকে শুরু করার জন্য পুরোপুরি বাইরে out যদি হয় তবে পরবর্তী ইনপুট লাইন দিয়ে।
    • যদি tএস্টটি সফল না হয় তবে আমরা bআবার :tওপ লেবেলটিতে ফিরে যাব এবং ইনপুটটির অন্য লাইনটির জন্য পুনরাবৃত্তি করবো - সম্ভবত আরও $matchকিছুটা জড়ো হওয়ার সময় লুপটি শুরু হবে $A
  • আমরা যদি অতীত একটি পেতে $matchফাংশন লুপ, তাহলে আমরা চেষ্টা করব pদ্রণ $শেষ লাইনটি যদি এই তা না হয়, আর যদি !চেষ্টা করবেন s///জন্য ubstitute &নিজেই $Bতম \nপ্যাটার্ন স্থান ewline অক্ষর।
    • আমরা tএটিও করব এবং এটি সফল হলে আমরা :Pরিন্ট লেবেলে শাখা করব ।
    • যদি না হয় তবে আমরা ফিরে শাখা করব :tএবং বাফারে আর একটি ইনপুট লাইন যুক্ত করব।
  • আমরা যদি করতে এটি :Pদ্রণ আমরা করব Pতারপর দ্রণ Dপ্রথম পর্যন্ত elete \nপ্যাটার্ন স্থান ewline দিলে যা থাকে সঙ্গে উপর থেকে স্ক্রিপ্ট পুনরায় আরম্ভ করুন।

এবং তাই এই সময়, যদি আমরা করছিলাম A=2 B=2 match=5; seq 5 | sed...

:Pরিন্টে প্রথম পুনরাবৃত্তির জন্য প্যাটার্ন স্পেসটি দেখতে পাবেন:

^1\n2\n3$

এবং এইভাবে এটি এর sedপূর্বের $Bবাফার সংগ্রহ করে । এবং সুতরাং এটি সংগ্রহ করা ইনপুট পিছনেsed আউটপুট- $Bঅ্যাকাউন্টিং লাইনগুলিতে মুদ্রণ করে । এর অর্থ এই যে, আমাদের পূর্ববর্তী উদাহরণ দেওয়া হবে দ্রণ আউটপুট, এবং তারপর elete এবং একটি প্যাটার্ন স্থান যা দেখে মনে হচ্ছে স্ক্রিপ্ট উপরের ফেরত পাঠাতে:sedP1D

^2\n3$

... এবং স্ক্রিপ্টের শীর্ষে Nএক্সপুট ইনপুট লাইনটি পুনরুদ্ধার করা হয়েছে এবং তাই পরবর্তী পুনরাবৃত্তিটি দেখে মনে হচ্ছে:

^2\n3\n4$

এবং তাই আমরা যখন ইনপুটটির প্রথম উপস্থিতিটি পাই 5, তখন প্যাটার্ন স্পেসটি আসলে দেখতে দেখতে:

^3\n4\n5$

তারপরে Dএকাদশ লুপটি লাথি মারে এবং এটির মাধ্যমে এটি দেখতে মনে হয়:

^5$

এবং যখন Nএক্সট্রা ইনপুট লাইনটি টানা হয় তখন ইওএফ sedহিট হয় এবং প্রস্থান করে। ততক্ষণে এটি কেবলমাত্র P1 এবং 2 এর লাইনটি ছড়িয়ে দিয়েছে ।

এখানে একটি উদাহরণ রান করুন:

A=8 B=7 match='[24689]0'
seq 100 |
sed -ne:t -e"/\n.*$match/D" \
    -e'$!N;//D;/'"$match/{" \
            -e"s/\n/&/$A;t" \
            -e'$q;bt' -e\}  \
    -e's/\n/&/'"$B;tP"      \
    -e'$!bt' -e:P  -e'P;D'

যে মুদ্রণ:

1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100

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

4
@ আমেলিও - এটি যে কোনও আকারের প্রবাহের সাথে কাজ করবে এবং কাজ করার জন্য এটি ফাইলটি পড়ার দরকার নেই। সবচেয়ে বড় কর্মক্ষমতা ফ্যাক্টর আকার $Aএবং / অথবা $B। আপনি এই সংখ্যাগুলি যত বড় করবেন, ধীরে ধীরে এটি পাবেন - তবে আপনি এগুলি যুক্তিসঙ্গতভাবে বড় করতে পারেন।
মাইকজার্ভ

1
@ আমেলিওভাজ্কেজ-রেইনা - আপনি যদি পুরোনোটি ব্যবহার করেন তবে এটি আরও ভাল I
মাইকসার্ভ

11

আপনি যে ফাইলটি বাদ দিতে চান তার ঠিক সেই অংশগুলির gnu grepসাথে -Aএবং -Bমুদ্রণ করতে ব্যবহার করতে পারেন -nতবে লাইন নম্বরগুলি মুদ্রণের জন্য সুইচ যুক্ত করুন এবং তারপরে আউটপুটটিকে ফর্ম্যাট করুন এবং sedসেই লাইনগুলি মুছতে কমান্ড স্ক্রিপ্ট হিসাবে এটি পাস করুন :

grep -n -A1 -B2 PATTERN infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile

এটাও প্রেরণ প্যাটার্নের ফাইলগুলির সাথে কাজ করা উচিত grepমাধ্যমে -fযেমন:

grep -n -A1 -B2 -f patterns infile | \
sed -n 's/^\([0-9]\{1,\}\).*/\1d/p' | \
sed -f - infile

আমি এই সামান্য অপ্টিমাইজ করা যেতে পারে যদি এটি রেঞ্জ মধ্যে কোনো বা তিনটি পরপর লাইন সংখ্যা ধসে যেমন যাতে আছে মনে 2,6dপরিবর্তে 2d;3d;4d;5d;6d... যদিও এটা এরকম অধিকারী না যদি ইনপুট মাত্র কয়েক ম্যাচ হয়েছে।


অন্যান্য উপায় যা লাইন ক্রম সংরক্ষণ করে না এবং সম্ভবত ধীর হয়: এর
সাথে comm:

comm -13 <(grep PATTERN -A1 -B2 <(nl -ba -nrz -s: infile) | sort) \
<(nl -ba -nrz -s: infile | sort) | cut -d: -f2-

commবাছাই করা ইনপুট প্রয়োজন যার অর্থ লাইন অর্ডারটি চূড়ান্ত আউটপুটে সংরক্ষণ করা হবে না (যদি আপনার ফাইলটি ইতিমধ্যে সাজানো না থাকে) সুতরাং nlবাছাইয়ের আগে লাইনগুলি সংখ্যায় ব্যবহার করতে ব্যবহৃত হয়, comm -13কেবল ২ য় ফাইলের অনন্য লাইনগুলি মুদ্রণ করে তারপরে যুক্ত cutহওয়া অংশটি সরিয়ে ফেলবে nl(এটি, প্রথম ক্ষেত্র এবং ডিলিমিটার :) এর
সাথে join:

join -t: -j1 -v1 <(nl -ba -nrz -s:  infile | sort) \
<(grep PATTERN -A1 -B2 <(nl -ba -nrz -s:  infile) | sort) | cut -d: -f2-

ধন্যবাদ ডন! তাত্ক্ষণিক প্রশ্ন, আপনি কি সমাধানটির সাথে commমূলটির সাথে দ্রুততর সমাধান আশা করতে পারবেন sedএবং grep?
আমেলিও ভাজকেজ-রেইনা

1
@ অ্যামেলিওভাজেকেজ-রেইনা - আমি মনে করি না যে এটি এখনও মাইকের সমাধানের বিপরীতে যেহেতু ইনপুট ফাইলটি দু'বার পড়বে (প্লাস এটি কিছু বাছাই করে) কেবল একবার ফাইলটি প্রক্রিয়া করে।
don_crissti

9

আপনি যদি ব্যবহার করতে আপত্তি না করেন vim:

$ export PAT=fff A=1 B=2
$ vim -Nes "+g/${PAT}/.-${B},.+${A}d" '+w !tee' '+q!' foo
aaa
bbb
ccc
hhh
iii
  • -Nesঅ-সামঞ্জস্যপূর্ণ, নীরব প্রাক্তন মোড চালু করে। স্ক্রিপ্টিং জন্য দরকারী।
  • +{command}ভিমে {command}ফাইলটি চালানোর জন্য বলুন ।
  • g/${PAT}/- সমস্ত লাইন মিলছে /fff/। যদি এই প্যাটার্নটিতে নিয়মিত অভিব্যক্তি বিশেষ অক্ষর থাকে যা আপনি সেভাবে আচরণের ইচ্ছা করেন নি তবে এটি জটিল হয়ে ওঠে।
  • .-${B} - এই এক উপরের 1 লাইন থেকে
  • .+${A}- এই একের নীচে 2 লাইন ( :he cmdline-rangesএই দুটি জন্য দেখুন )
  • d - লাইন মুছুন।
  • +w !tee তারপরে স্ট্যান্ডার্ড আউটপুট লিখুন।
  • +q! পরিবর্তনগুলি সংরক্ষণ না করে ছাড়ুন।

আপনি ভেরিয়েবলগুলি এড়িয়ে যেতে পারেন এবং প্যাটার্ন এবং সংখ্যাগুলি সরাসরি ব্যবহার করতে পারেন। আমি এগুলি কেবল উদ্দেশ্য স্পষ্টতার জন্য ব্যবহার করেছি।


3

কীভাবে (জিএনইউ ব্যবহার করে grepএবং bash):

$ grep -vFf - file.txt < <(grep -B2 -A1 'fff' file.txt)
aaa
bbb
ccc
hhh
iii

এখানে আমরা যে লাইনগুলি ফেলে দিতে হবে তা সন্ধান করছি grep -B2 -A1 'fff' file.txt, তারপরে এগুলি প্রত্যাখ্যান করে কাঙ্ক্ষিত লাইনগুলি অনুসন্ধান করতে একটি ইনপুট ফাইল হিসাবে এটি ব্যবহার করুন।


হুম, এটি আমার মেশিনে কোনও কিছুই আউটপুট দেয় না (ওএস এক্স)
আমেলিও ওয়াজকেজ-রেইনা

@ আমেলিও ওয়াজকেজ-রেইনা এর জন্য দুঃখিত ... আমি আপনার ওএস আগে জানতাম না .. তবে আমি উবুন্টুতে এটি পরীক্ষা করেছি ..
হিমাইল

2
এটির kos(এখন মুছে ফেলা) সমাধানের মতো একই সমস্যা হবে যেন ইনপুট ফাইলে সদৃশ লাইন রয়েছে এবং তাদের মধ্যে কিছুটি রেঞ্জের বাইরে চলে যায় এবং অন্যরা সেই পরিসরের অভ্যন্তরে থাকে এটি তাদের সমস্তটি মুছে ফেলবে। এছাড়াও, প্যাটার্নের একাধিক সংঘটনগুলির সাথে , যদি --ইনপুট ফাইলের মতো লাইন থাকে ( সীমার বাইরে) তবে এটি মুছে ফেলা হবে কারণ যখন আরও একটি লাইন প্যাটার্নের সাথে মিলছে তখন ডিলিমিটারের আউটপুটে --উপস্থিত হবে (দ্বিতীয়টি অত্যন্ত সম্ভাবনাময় তবে মূল্যবান উল্লেখ আমি অনুমান)। grep
don_crissti

@ ডন_ক্রিসটি ধন্যবাদ..আপনি ঠিক বলেছেন..যদি আমি ওপির উদাহরণটি আক্ষরিক অর্থে
নিচ্ছিলাম

1

অস্থায়ী ফাইলগুলি ব্যবহার করে আপনি যথেষ্ট পরিমাণে পৌঁছাতে পারবেন:

my_file=file.txt #or =$1 if in a script

#create a file with all the lines to discard, numbered
grep -n -B1 -A5 TBD "$my_file" |cut -d\  -f1|tr -d ':-'|sort > /tmp/___"$my_file"_unpair

#number all the lines
nl -nln "$my_file"|cut -d\  -f1|tr -d ':-'|sort >  /tmp/___"$my_file"_all

#join the two, creating a file with the numbers of all the lines to keep
#i.e. of those _not_ found in the "unpair" file
join -v2  /tmp/___"$my_file"_unpair /tmp/___"$my_file"_all|sort -n > /tmp/___"$my_file"_lines_to_keep

#eventually use these line numbers to extract lines from the original file
nl -nln $my_file|join - /tmp/___"$my_file"_lines_to_keep |cut -d\  -f2- > "$my_file"_clean

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


1

এছাড়াও, আপনি যদি নির্দিষ্ট প্রদত্ত চিহ্নিতকারীর সামনে কিছু লাইন বাদ দিতে চান তবে আপনি এটি ব্যবহার করতে পারেন:

awk -v nlines=2 '/Exception/ {for (i=0; i<nlines; i++) {getline}; next} 1'

( /programming//a/1492538 এ গ্লেন জ্যাকম্যান )

কিছু কমান্ড পাইপ দিয়ে আপনি আচরণের আগে / পরে পেতে পারেন:

awk -v nlines_after=5 '/EXCEPTION/ {for (i=0; i<nlines_after; i++) {getline};print "EXCEPTION" ;next} 1' filename.txt|\
tac|\
awk -v nlines_before=1 '/EXCEPTION/ {for (i=0; i<nlines_before; i++) {getline}; next} 1'|\
tac

1
উজ্জ্বল, awkযখন আপনি আগে লাইনগুলিকে প্রভাবিত করতে চান এবং ফলাফলটি পুনরায় বিপরীত করতে চান তখন নিম্নলিখিত লাইনগুলি হ্যান্ডেল করতে বিপরীত ফাইলটিতে ব্যবহার করুন।
কর্মকাজে

0

এটি সম্পাদন করার একটি উপায়, সম্ভবত সবচেয়ে সহজ উপায় হ'ল একটি পরিবর্তনশীল তৈরি করা এবং নিম্নলিখিতগুলি করা:

grep -v "$(grep "fff" -A1 -B2 file.txt)" file.txt

এইভাবে আপনার এখনও আপনার কাঠামো রয়েছে। আপনি সহজেই এক লাইনার থেকে দেখতে পাচ্ছেন যে আপনি কী সরাতে চাইছেন।

$ grep -v "$(grep "fff" -A1 -B2 file.txt)" file.txt
aaa
bbb
ccc
hhh
iii

হেইমাইলের মতো একই সমাধান, এবং ডন_ক্রিসটি দ্বারা বর্ণিত একই সমস্যা: কোস-এর (এখন মুছে ফেলা) সমাধানের মতো একই সমস্যাটি হবে যেমন ইনপুট ফাইলে নকল লাইন রয়েছে এবং তাদের মধ্যে কিছুগুলি রেঞ্জের বাইরে চলে যায় এবং অন্যরাও সেই সীমার অভ্যন্তরে থাকে এটি তাদের সমস্ত মুছে ফেলবে। এছাড়াও, প্যাটার্নের একাধিক সংঘটনগুলির সাথে, যদি লাইন থাকে তবে - ইনপুট ফাইলে (সীমার বাইরে) এটি মুছে ফেলবে কারণ ডিলিমিটার - গ্রেপের আউটপুটে উপস্থিত হয় যখন আরও একটি লাইন প্যাটার্নের সাথে মিলে যায় (দ্বিতীয়টি অত্যন্ত উচ্চতর হয়) অসম্ভব তবে উল্লেখ করার মতো অনুমান আমি)।
বোডো থিয়েসন

0

যদি কেবল 1 টি ম্যাচ থাকে:

A=1; B=2; n=$(grep -n 'fff' file.txt | cut -d: -f1)
head -n $((n-B-1)) file.txt ; tail -n +$((n+A+1)) file.txt

অন্যথায় (অ্যাডক):

# -vA=a -vB=b -vpattern=pat must be provided
BEGIN{

    # add file again. assume single file
    ARGV[ARGC]=ARGV[ARGC-1]
    ++ARGC
}

# the same as grep -An -Bn pattern
FNR==NR && $0 ~ pattern{
    for (i = 0; i <= B; ++i)
        a[NR-i]++
    for (i = 1; i <= A; ++i)
        a[NR+i]++
}

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