অন্য ফাইল নয় এমন একটি ফাইলের লাইনগুলি খুঁজে পাওয়ার দ্রুত উপায়?


241

আমার কাছে দুটি বড় ফাইল (ফাইলের নামের সেট) রয়েছে। মোটামুটি প্রতিটি ফাইলের 30.000 লাইন। আমি ফাইল 1-তে লাইনগুলি খুঁজে পাওয়ার একটি দ্রুত উপায় সন্ধান করার চেষ্টা করছি যা ফাইল 2 তে উপস্থিত নেই।

উদাহরণস্বরূপ, এটি যদি ফাইল 1 হয়:

line1
line2
line3

এবং এটি ফাইল 2:

line1
line4
line5

তারপরে আমার ফলাফল / আউটপুটটি হওয়া উচিত:

line2
line3

এইটা কাজ করে:

grep -v -f file2 file1

তবে আমার বড় ফাইলগুলিতে এটি খুব ধীর গতির হয়।

আমি সন্দেহ করি ডিফ () ব্যবহার করে এটি করার একটি ভাল উপায় আছে তবে আউটপুটটি কেবলমাত্র লাইন হওয়া উচিত , অন্য কিছুই নয় এবং এর জন্য আমি কোনও স্যুইচ খুঁজে পাচ্ছি না।

ব্যাশ এবং বেসিক লিনাক্স বাইনারিগুলি ব্যবহার করে কেউ কি আমাকে এটির দ্রুত উপায় খুঁজে পেতে সহায়তা করতে পারে?

সম্পাদনা: আমার নিজের প্রশ্ন অনুসরণ করতে, আমি এখন পর্যন্ত ডিফ () ব্যবহার করে এটি খুঁজে পাওয়ার সেরা উপায়:

diff file2 file1 | grep '^>' | sed 's/^>\ //'

অবশ্যই, আরও ভাল উপায় আছে?


1
এটি দ্রুত হলে আপনি এটি চেষ্টা করতে পারেন:awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt
ক্যান্ট


4
Grep -v -f file2 file1 সম্পর্কে বলার জন্য ধন্যবাদ
রাহুল প্রসাদ


হ্রাস সরঞ্জাম সেট সহ সহজ উপায় cat file1 file2 file2 | sort | uniq --unique:, নীচে আমার উত্তর দেখুন।
ওন্দ্র Žižka

উত্তর:


233

আপনি জিএনইউ diffআউটপুটে পুরানো / নতুন / অপরিবর্তিত লাইনের বিন্যাস নিয়ন্ত্রণ করে এটি অর্জন করতে পারেন :

diff --new-line-format="" --unchanged-line-format=""  file1 file2

এটি কাজ করার জন্য ইনপুট ফাইলগুলি বাছাই করা উচিত । সঙ্গে bash(এবং zsh) আপনি ইন-জায়গা প্রক্রিয়া প্রতিকল্পন সঙ্গে সাজাতে পারেন <( ):

diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)

উপরের নতুন এবং অপরিবর্তিত রেখাগুলি দমন করা হয়, সুতরাং কেবল পরিবর্তিত (যেমন আপনার ক্ষেত্রে সরানো রেখাগুলি) আউটপুট। এছাড়াও আপনি একটি কয়েক ব্যবহার করতে পারেন diffবিকল্প অন্যান্য সমাধানের প্রস্তাব না, যেমন -iমামলা, বা বিভিন্ন হোয়াইটস্পেস অপশন (এড়িয়ে যেতে -E, -b, -vইত্যাদি) কম কঠোর মিলের জন্য।


ব্যাখ্যা

অপশন --new-line-format, --old-line-formatএবং --unchanged-line-formatআপনি নিয়ন্ত্রণ পথ দিন diffপার্থক্য, অনুরূপ ফরম্যাট printfবিন্যাস নির্দিষ্টকরী। এই বিকল্পগুলি যথাক্রমে নতুন (যুক্ত), পুরানো (সরানো) এবং অপরিবর্তিত রেখাগুলি বিন্যাস করে। একটিকে খালি সেট করা "" ধরণের লাইনের আউটপুট আটকায়।

আপনি যদি ইউনিফাইড ডিফার্ট ফর্ম্যাটটির সাথে পরিচিত হন তবে আপনি এটি সহ আংশিকভাবে এটি আবার তৈরি করতে পারেন:

diff --old-line-format="-%L" --unchanged-line-format=" %L" \
     --new-line-format="+%L" file1 file2

%Lসুনির্দিষ্টভাবে উল্লেখ করা প্রশ্নে লাইন, এবং আমরা সঙ্গে "+ +" "প্রতিটি পূর্বে ভী -" বা "", মত diff -u (নোট যে এটি শুধুমাত্র আউটপুট পার্থক্য, এটা অভাব আছে --- +++এবং @@লাইন প্রতিটি দলবদ্ধ পরিবর্তন উপরের)। আপনি প্রতিটি লাইনের সাথে সংখ্যার মতো অন্যান্য দরকারী জিনিসগুলি করতেও এটি ব্যবহার করতে পারেন %dn


diffপদ্ধতি (অন্যান্য পরামর্শের সঙ্গে বরাবর commএবং join) শুধুমাত্র সঙ্গে প্রত্যাশিত আউটপুট উত্পাদন সাজানো , ইনপুট যদিও আপনি ব্যবহার করতে পারেন <(sort ...)জায়গায় সাজাতে হয়। এখানে একটি সাধারণ awk(নওক) স্ক্রিপ্ট (কনসোলবক্সের উত্তরে লিঙ্কযুক্ত লিপিগুলি দ্বারা অনুপ্রাণিত) যা নির্বিচারে আদেশ করা ইনপুট ফাইলগুলি গ্রহণ করে এবং ফাইল 1-এ প্রদর্শিত ক্রমটি অনুপস্থিত রেখাগুলি আউটপুট দেয়।

# output lines in file1 that are not in file2
BEGIN { FS="" }                         # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; }     # file1, index by lineno
(NR!=FNR) { ss2[$0]++; }                # file2, index by string
END {
    for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}

এটি লাইন-সংখ্যা সূচিকৃত অ্যারেতে লাইন দিয়ে ফাইল 1 লাইনের ll1[]সম্পূর্ণ সামগ্রী এবং লাইন-কন্টেন্ট ইনডেক্সড এসোসিয়েটিভ অ্যারে লাইনে ফাইল 2 লাইনের সম্পূর্ণ সামগ্রী সংরক্ষণ করে ss2[]। উভয় ফাইল পড়ার পরে, পুনরাবৃত্তি হবে ll1এবং ফাইল 1- inএ লাইনটি ফাইল 2 এ উপস্থিত রয়েছে কিনা তা নির্ধারণ করতে অপারেটরটি ব্যবহার করুন । ( diffডুপ্লিকেট থাকলে এর পদ্ধতিতে আলাদা আলাদা আউটপুট থাকবে ))

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

BEGIN { FS="" }
(NR==FNR) {  # file1, index by lineno and string
  ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) {  # file2
  if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
  for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}

উপরের দুটি ফাইল অ্যারের মধ্যে ফাইল 1 এর সম্পূর্ণ বিষয়বস্তু সংরক্ষণ করে, একটি লাইন নম্বর ll1[]দ্বারা সূচিত, একটি লাইন সামগ্রী দ্বারা সূচিত ss1[]। তারপরে ফাইল 2 পড়ার সাথে সাথে প্রতিটি মিলের লাইনটি মুছে ফেলা হয় ll1[]এবং থেকে ss1[]। ফাইল 1 থেকে বাকি লাইনগুলি আউটপুট, মূল ক্রম সংরক্ষণ করে।

এক্ষেত্রে সমস্যা হিসাবে যেমন বলা হয়েছে, আপনি জিএনইউ (ফিল্টারিং একটি জিএনইউ এক্সটেনশন) ব্যবহার করে বিভাজন এবং বিজয় অর্জন করতে পারেন split, ফাইল 1 এর খণ্ডগুলি এবং বারবার সম্পূর্ণ ফাইল 2 পড়া নিয়ে বারবার রান করা:

split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1

উল্লেখ্য ব্যবহার এবং বসানো -মানে stdinউপর gawkকমান্ড লাইন। এটি splitপ্রতি-অনুরোধে 20000 লাইনের অংশগুলিতে ফাইল 1 থেকে সরবরাহ করা হয় ।

অ গনুহ সিস্টেমে ব্যবহারকারীদের জন্য, আছে প্রায় অবশ্যই একটি গনুহ coreutils প্যাকেজ আপনি পেতে পারেন অংশ হিসেবে উপর ওএসএক্স সহ, অ্যাপল Xcode সরঞ্জাম যা গনুহ প্রদান করে diff, awkযদিও শুধুমাত্র একটি POSIX / বাসদ splitবদলে গনুহ সংস্করণ।


1
এটি আমার প্রয়োজন ঠিক ঠিক কাজটি করে, প্রচুর গ্রেপ দ্বারা নেওয়া সময়ের একটি ক্ষুদ্র ভগ্নাংশে। ধন্যবাদ!
নিলস 2000


আমাদের মধ্যে কয়েকজন
জিএনইউতে

1
আমি ধরে নিলাম আপনি এর অর্থ বোঝাতে চাইছেন diff: সাধারণভাবে ইনপুট ফাইলগুলি আলাদা হবে, diffসেই ক্ষেত্রে 1 টি ফিরে আসে । এটি একটি বোনাস বিবেচনা করুন ;-) আপনি যদি শেল স্ক্রিপ্ট 0 এবং 1 তে পরীক্ষা করে থাকেন তবে প্রত্যাশিত প্রস্থান কোডগুলি হয়, 2 একটি সমস্যা নির্দেশ করে।
মিঃ স্পুর্যাটিক

1
@ মিঃ স্পুর্যাটিক আহা হ্যাঁ, এখন এটি আমি খুঁজে পেয়েছি man diff। ধন্যবাদ!
আর্কিওসুডোয়েরাস

244

Comm কমান্ড (ছোট "সাধারণ" জন্য) দরকারী হতে পারেcomm - compare two sorted files line by line

#find lines only in file1
comm -23 file1 file2 

#find lines only in file2
comm -13 file1 file2 

#find lines common to both files
comm -12 file1 file2 

manফাইল আসলে এই জন্য বেশ পাঠযোগ্য।


6
ওএসএক্সে নির্বিঘ্নে কাজ করে।
পিসারুক

40
বাছাই করা ইনপুটটির প্রয়োজনীয়তা সম্ভবত হাইলাইট করা উচিত।
ট্রিপলি

21
commইনপুটটি বাছাই করা হয়েছে যাচাই করার একটি বিকল্প রয়েছে, --check-order(যা এটি যাইহোক মনে হয় তবে এই বিকল্পটি চালিয়ে যাওয়ার পরিবর্তে ত্রুটি ঘটায়)। তবে ফাইলগুলি বাছাই করতে, কেবল করুন: com -23 <(sort file1) <(sort file2)এবং আরও
মাইকেল

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

23

কনসোলবক্সের মতো প্রস্তাবিত, পোস্টারগুলি গ্রেপ সলিউশন

grep -v -f file2 file1

-Fনিয়মিত প্রকাশের পরিবর্তে প্যাটার্নগুলিকে স্থির স্ট্রিং হিসাবে গণ্য করার জন্য বিকল্পটি যুক্ত করতে পারলে প্রকৃতপক্ষে দুর্দান্ত (দ্রুত) কাজ করে। আমার তুলনা করতে হবে এমন pair 1000 লাইন ফাইল তালিকার একটি জুড়ে আমি এটি যাচাই করেছি। -Fএটি গ্রাফিক আউটপুটটিতে পুনর্নির্দেশের সময় 0.031 স (রিয়েল) সাথে নিয়েছিল, যখন এটি ছাড়া 2.278 এস (রিয়েল) লাগবে wc -l

এই পরীক্ষাগুলিতেও -xস্যুইচ অন্তর্ভুক্ত ছিল , যা ফাইল 2-এ লাইন রয়েছে যা ফাইলের 1 বা একাধিক লাইনের অংশের সাথে মিলে যায় এমন ক্ষেত্রে সম্পূর্ণ নির্ভুলতা নিশ্চিত করার জন্য সমাধানের প্রয়োজনীয় অংশ।

সুতরাং একটি সমাধান যা ইনপুটগুলি বাছাইয়ের প্রয়োজন হয় না তা দ্রুত, নমনীয় (কেস সংবেদনশীলতা ইত্যাদি):

grep -F -x -v -f file2 file1

এটি গ্রেপের সমস্ত সংস্করণগুলির সাথে কাজ করে না, উদাহরণস্বরূপ এটি ম্যাকোজে ব্যর্থ হয়, যেখানে ফাইল 1-এ একটি লাইন ফাইল 2 তে উপস্থিত না হিসাবে প্রদর্শিত হবে, যদিও এটি অন্য লাইনটির সাথে মেলে যদি এটির একটি স্ট্রিং থাকে । বিকল্প হিসাবে আপনি এই সমাধানটি ব্যবহার করার জন্য ম্যাকোজে জিএনইউ গ্রেপ ইনস্টল করতে পারেন ।


হ্যাঁ, এটি কাজ করে কিন্তু এর সাথেও এটি -Fভাল স্কেল হয় না।
মোলম্বি

এটি তাত্পর্যপূর্ণ নয়, আমি
হাল ছাড়ার

প্রকৃতপক্ষে, এই পথটি কমের চেয়েও ধীর গতিতে, কারণ এইটি অনরসোর্টড ফাইলগুলি হস্তান্তর করতে পারে তাই তাকে বাছাই করে নামিয়ে নেওয়া, কম বাছাইয়ের সুবিধা গ্রহণ করে
workplaylifecycle

@ ওয়ার্কপ্লেলিফাইসাইক্যাল বাছাইয়ের জন্য আপনাকে সময় যোগ করতে হবে যা খুব বড়র জন্য বাধা হতে পারে file2
rwst

তবে -xবিকল্প সহ গ্রেপ স্পষ্টতই আরও মেমরি ব্যবহার করে। একটি সঙ্গে file26-10 এর ধারণকারী 180M শব্দ বাইট আমার প্রক্রিয়া পেয়েছিলাম Killedএকটি 32GB র্যাম মেশিনে ...
rwst

11

বাছাই এবং ভিন্ন হিসাবে গতি কি?

sort file1 -u > file1.sorted
sort file2 -u > file2.sorted
diff file1.sorted file2.sorted

1
আলাদা করার আগে ফাইলগুলি বাছাই করার প্রয়োজনীয়তা সম্পর্কে মনে করিয়ে দেওয়ার জন্য ধন্যবাদ Thanks সাজান + ডিফ অনেক দ্রুত is
নিলস 2000

4
একটি লাইনার ;-) ডিফার <(সাজান ফাইল 1 -u) <(বাছাই করা
ফাইল 2

11

আপনি "অভিনব সরঞ্জামগুলি", যেমন কিছু ন্যূনতম লিনাক্স বিতরণে সংক্ষিপ্ত হন, তাহলে, শুধু সঙ্গে একটি সমাধান পাওয়া যাবে cat, sortএবং uniq:

cat includes.txt excludes.txt excludes.txt | sort | uniq --unique

টেস্ট:

seq 1 1 7 | sort --random-sort > includes.txt
seq 3 1 9 | sort --random-sort > excludes.txt
cat includes.txt excludes.txt excludes.txt | sort | uniq --unique

# Output:
1
2    

এই হল অপেক্ষাকৃত দ্রুত তুলনায় grep


1
দ্রষ্টব্য - কিছু বাস্তবায়ন --uniqueবিকল্পটিকে স্বীকৃতি দেবে না । আপনি এর জন্য | uniq -u
মানকযুক্ত পসিক্স

1
উদাহরণস্বরূপ, "2" কোথা থেকে এসেছে?
নীল 2000

1
@ নিলস 2000, seq 1 1 71 থেকে সংখ্যা বাড়িয়ে 1, বর্ধিত 1 দিয়ে 7 পর্যন্ত, অর্থাৎ 1 2 3 4 5 6 7. এবং ঠিক আছে আপনার 2 আছে!
এরিক লিগ্রে

5
$ join -v 1 -t '' file1 file2
line2
line3

-tনিশ্চিত করুন যে এটি সমগ্র লাইন তুলনা, আপনি যদি লাইনের কিছু একটি স্থান ছিল তোলে।


লাইক comm, joinউভয় ইনপুট লাইন ক্ষেত্রের বাছাই করা প্রয়োজন যা আপনি যোগদানের ক্রিয়াকলাপটি করছেন।
ট্রিপলি

4

আপনি পাইথন ব্যবহার করতে পারেন:

python -c '
lines_to_remove = set()
with open("file2", "r") as f:
    for line in f.readlines():
        lines_to_remove.add(line.strip())

with open("f1", "r") as f:
    for line in f.readlines():
        if line.strip() not in lines_to_remove:
            print(line.strip())
'

4

ব্যবহার করুন combineথেকে moreutilsপ্যাকেজ, একটি সেট উপযোগ যে সমর্থন not, and, or, xorঅপারেশন

combine file1 not file2

উদাহরণস্বরূপ, আমাকে ফাইললাইনগুলিতে লাইন দিন তবে ফাইল 2 তে নেই

অথবা আমাকে ফাইল 2-এ বিয়োগ লাইনগুলিতে লাইন দিন

দ্রষ্টব্য: combine কোনও ক্রিয়াকলাপ সম্পাদন করার আগে উভয় ফাইলে অনবদ্য লাইনগুলি সাজানো এবং সন্ধান করে তবে diffতা হয় না। সুতরাং আপনি diffএবং এর আউটপুট মধ্যে পার্থক্য খুঁজে পেতে পারেন combine

সুতরাং বাস্তবে আপনি বলছেন

ফাইল 1 এবং ফাইল 2 এ আলাদা লাইনগুলি সন্ধান করুন এবং তারপরে আমাকে ফাইল 2-এ বিয়োগ লাইনগুলিতে লাইন দিন

আমার অভিজ্ঞতায় এটি অন্যান্য বিকল্পের চেয়ে অনেক দ্রুত


2

গ্রেগ-এ fgrep ব্যবহার করা বা -F বিকল্প ব্যবহার করা সাহায্য করতে পারে। তবে দ্রুত গণনার জন্য আপনি Awk ব্যবহার করতে পারেন।

আপনি এই অ্যাওক পদ্ধতিগুলির মধ্যে একটি চেষ্টা করতে পারেন:

http://www.linuxquestions.org/questions/programming-9/grep-for-huge-files-826030/#post4066219


2
+1 এটিই একমাত্র উত্তর যা সাজানোর জন্য ইনপুটগুলির প্রয়োজন হয় না। স্পষ্টতই ওপি সেই প্রয়োজনে খুশি ছিল, এটি অনেক বাস্তব-বিশ্ব পরিস্থিতিতে একটি অগ্রহণযোগ্য বাধা।
ট্রিপলি

1

আমি সাধারণত যেভাবে এটি করি তা --suppress-common-linesপতাকা ব্যবহার করা হয় , তবে নোট করুন যে এটি কেবল তখনই কাজ করে যদি আপনি এটি পাশাপাশি বসে বিন্যাসে করেন।

diff -y --suppress-common-lines file1.txt file2.txt


0

আমি খুঁজে পেয়েছি যে আমার জন্য একটি সাধারণ ব্যবহার করে এবং লুপ স্টেটমেন্ট পুরোপুরি কাজ করে।

for i in $(cat file2);do if [ $(grep -i $i file1) ];then echo "$i found" >>Matching_lines.txt;else echo "$i missing" >>missing_lines.txt ;fi;done

2
DontReadLinesWithfor দেখুন । এছাড়াও, যদি আপনার কোনও grepফলাফল একাধিক শব্দের কাছে প্রসারিত হয় বা আপনার কোনও file2এন্ট্রি শেল দ্বারা গ্লোব হিসাবে বিবেচনা করা যায় তবে এই কোডটি খুব খারাপভাবে আচরণ করবে ।
চার্লস ডাফি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.