बैश से "समूह द्वारा" अनुकरण करने का सबसे अच्छा तरीका है?


231

मान लें कि आपके पास एक फ़ाइल है जिसमें IP पते, प्रत्येक पंक्ति में एक पता है:

10.0.10.1
10.0.10.1
10.0.10.3
10.0.10.2
10.0.10.1

आपको एक शेल स्क्रिप्ट की आवश्यकता है जो प्रत्येक आईपी पते के लिए मायने रखता है कि यह फ़ाइल में कितनी बार दिखाई देता है। पिछले इनपुट के लिए आपको निम्न आउटपुट की आवश्यकता है:

10.0.10.1 3
10.0.10.2 1
10.0.10.3 1

ऐसा करने का एक तरीका है:

cat ip_addresses |uniq |while read ip
do
    echo -n $ip" "
    grep -c $ip ip_addresses
done

हालांकि यह वास्तव में कुशल होने से बहुत दूर है।

आप बैश का उपयोग करके इस समस्या को अधिक कुशलता से कैसे हल करेंगे?

(जोड़ने के लिए एक बात: मुझे पता है कि इसे पर्ल या ऑक से हल किया जा सकता है, मैं बैश में बेहतर समाधान में दिलचस्पी रखता हूं, उन भाषाओं में नहीं।)

अतिरिक्त जानकारी:

मान लीजिए कि स्रोत फ़ाइल 5GB है और एल्गोरिथ्म चलाने वाली मशीन में 4GB है। तो सॉर्ट एक कुशल समाधान नहीं है, न ही फाइल को एक से अधिक बार पढ़ना है।

मुझे हैशटेबल जैसा समाधान पसंद आया - कोई भी उस समाधान में सुधार प्रदान कर सकता है?

अतिरिक्त जानकारी # 2:

कुछ लोगों ने पूछा कि मैं इसे बैश में क्यों परेशान करूँगा जब यह आसान है जैसे कि पर्ल। कारण यह है कि जिस मशीन पर मुझे यह करना था, वह मेरे लिए उपलब्ध नहीं थी। यह एक कस्टम बिल्ट लाइनक्स मशीन थी, जिसके अधिकांश उपकरण मैं इस्तेमाल कर रहा था। और मुझे लगता है कि यह एक दिलचस्प समस्या थी।

तो कृपया, प्रश्न को दोष न दें, यदि आप इसे पसंद नहीं करते हैं तो बस इसे अनदेखा करें। :-)


मुझे लगता है कि काम के लिए बैश गलत उपकरण है। पर्ल शायद एक बेहतर समाधान होगा।
फ्रेंकोइस वोल्मारंस

जवाबों:


412
sort ip_addresses | uniq -c

यह गिनती पहले प्रिंट करेगा, लेकिन इसके अलावा यह वही होना चाहिए जो आप चाहते हैं।


71
जो तब आप "सॉर्ट -nr" पर पाइप कर सकते हैं, जो उच्चतम क्रम से सबसे कम गिनती तक अवरोही क्रम में क्रमबद्ध होता है। अर्थात्sort ip_addresses | uniq -c | sort -nr
ब्रैड पार्क्स

15
और sort ip_addresses | uniq -c | sort -nr | awk '{ print $2, $1 }'पहले कॉलम में आईपी एड्रेस पाने के लिए और दूसरे में काउंट करने के लिए।
रघु डोड्डा

सॉर्ट भाग के लिए एक और ट्वीक:sort -nr -k1,1
एन्द्रेजेज मार्टिना

50

त्वरित और गंदी विधि इस प्रकार है:

cat ip_addresses | sort -n | uniq -c

यदि आपको bash में मानों का उपयोग करने की आवश्यकता है, तो आप पूरे कमांड को bash वैरिएबल पर असाइन कर सकते हैं और फिर परिणामों के माध्यम से लूप कर सकते हैं।

पुनश्च

यदि सॉर्ट कमांड को छोड़ दिया जाता है, तो आपको सही परिणाम नहीं मिलेगा क्योंकि यूनीक केवल क्रमिक समान लाइनों को देखता है।


यह बहुत ही दक्षता-वार है, आपके पास अभी भी द्विघात व्यवहार है
विंको व्रसालोविक

द्विघात अर्थ हे (n ^ 2) ?? यह निश्चित रूप से सॉर्ट एल्गोरिथ्म पर निर्भर करेगा, यह इस तरह के बोगो-सॉर्ट का उपयोग करने की संभावना नहीं है।
paxdiablo

खैर, सबसे अच्छे मामले में यह O (n log (n)) होगा, जो दो पास से भी बदतर है (जो कि आपको एक हैवी हैश आधारित कार्यान्वयन के साथ मिलता है)। मुझे द्विघात के बजाय 'सुपरलाइनियर' कहना चाहिए था।
विन्को वर्सालोविक

और यह अभी भी उसी सीमा में है कि ओपी ने दक्षता में सुधार करने के लिए क्या कहा ...
विंको वर्सालोविच

11
uuoc, बिल्ली का बेकार उपयोग

22

मौजूदा फ़ील्ड के समूह के आधार पर, कई फ़ील्ड्स को संक्षेप में रखने के लिए, नीचे दिए गए उदाहरण का उपयोग करें: ($ 1, $ 2, $ 3, $ 4 अपनी आवश्यकताओं के अनुसार)

cat file

US|A|1000|2000
US|B|1000|2000
US|C|1000|2000
UK|1|1000|2000
UK|1|1000|2000
UK|1|1000|2000

awk 'BEGIN { FS=OFS=SUBSEP="|"}{arr[$1,$2]+=$3+$4 }END {for (i in arr) print i,arr[i]}' file

US|A|3000
US|B|3000
US|C|3000
UK|1|9000

2
+1 क्योंकि यह दिखाता है कि क्या करना है जब न केवल गिनती की आवश्यकता है
user829755

1
+1 क्योंकि sortऔर uniqगणना करना आसान है, लेकिन फ़ील्ड मानों की गणना / योग करने के लिए आपको मदद नहीं चाहिए। awk की अरेंज सिंटैक्स बहुत शक्तिशाली है और यहाँ समूहीकरण के लिए महत्वपूर्ण है। धन्यवाद!
12

1
एक और बात, देखना awk का है कि print2 ^ 31 से अधिक पूर्णांक मूल्यों के लिए समारोह 32 बिट के लिए 64 बिट्स पूर्णांकों downscale करने लगता है, तो आप उपयोग कर सकते हैं printfके साथ %.0fके बजाय प्रारूप printवहाँ
odony

1
संख्या जोड़ के बजाय स्ट्रिंग संयोजन के साथ "समूह द्वारा" की तलाश करने वाले लोग सफलता के arr[$1,$2]+=$3+$4साथ उदाहरणार्थ arr[$1,$2]=(arr[$1,$2] $3 "," $4). I needed this to provide a grouped-by-package list of files (two columns only) and used: गिरफ्तारी [$ 1] = (गिरफ्तारी [$ 1] $ 2) के साथ प्रतिस्थापित करेंगे ।
स्टीफन गौरिचोन

20

विहित समाधान एक अन्य प्रतिवादी द्वारा उल्लिखित है:

sort | uniq -c

यह पर्ल या अक्खड़ में जो लिखा जा सकता है, उससे छोटा और अधिक संक्षिप्त है।

आप लिखते हैं कि आप सॉर्ट का उपयोग नहीं करना चाहते हैं, क्योंकि डेटा का आकार मशीन के मुख्य मेमोरी आकार से बड़ा है। यूनिक्स सॉर्ट कमांड के कार्यान्वयन की गुणवत्ता को कम मत समझो। सॉर्ट करने के लिए 128k (कि 131,072 बाइट्स) मेमोरी (PDP-11) वाली मशीनों पर डेटा की बहुत बड़ी मात्रा (मूल एटी एंड टी के बिलिंग डेटा को संभालने के लिए) का उपयोग किया गया था। जब सॉर्ट एक पूर्व निर्धारित सीमा से अधिक डेटा का सामना करता है (अक्सर मशीन की मुख्य मेमोरी के आकार के करीब होता है) यह उस डेटा को सॉर्ट करता है जिसे उसने मुख्य मेमोरी में पढ़ा है और इसे एक अस्थायी फ़ाइल में लिखता है। यह फिर डेटा के अगले भाग के साथ कार्रवाई को दोहराता है। अंत में, यह उन मध्यवर्ती फ़ाइलों पर एक मर्ज सॉर्ट करता है। यह मशीन की मुख्य मेमोरी से कई गुना बड़े डेटा पर काम करने की अनुमति देता है।


खैर, यह अभी भी हैश की तुलना में खराब है, नहीं? क्या आप जानते हैं कि यदि डेटा मेमोरी में फिट बैठता है तो किस तरह का एल्गोरिथ्म सॉर्ट करता है? क्या यह संख्यात्मक डेटा मामले (-n विकल्प) में भिन्न है?
विन्को वर्सालोविच

यह इस बात पर निर्भर करता है कि किस तरह (1) लागू किया गया है। जीएनयू सॉर्ट (लिनक्स वितरण पर प्रयुक्त) और बीएसडी सॉर्ट दोनों सबसे उपयुक्त एल्गोरिदम का उपयोग करने के लिए बड़ी लंबाई तक जाते हैं।
डायोमिडिस स्पिनेलिस

9
cat ip_addresses | sort | uniq -c | sort -nr | awk '{print $2 " " $1}'

यह कमांड आपको वांछित आउटपुट देगा


4

ऐसा लगता है कि आपको रैखिक व्यवहार प्राप्त करने या द्विघात सुपरलाइनियर संस्करणों से चिपके रहने के लिए बैश में हैश का अनुकरण करने के लिए या तो बड़ी मात्रा में कोड का उपयोग करना होगा ।

उन संस्करणों में, सौआ का समाधान सबसे अच्छा (और सरल) है:

sort -n ip_addresses.txt | uniq -c

मैंने http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-11/0118.html पाया । लेकिन यह नरक के रूप में बदसूरत है ...


मैं सहमत हूँ। यह अब तक का सबसे अच्छा समाधान है और इसी तरह के समाधान पर्ल और ऑक में संभव हैं। क्या कोई किसी व्यक्ति को बैश में सफाई प्रदान कर सकता है?
जिज्सेन्स

मेरी जानकारी में नहीं। आप हैश का समर्थन करने वाली भाषाओं में बेहतर कार्यान्वयन प्राप्त कर सकते हैं, जहाँ आप मेरे $ ip (@ips) {$ हैश {$ ip} = $ हैश {$ ip} + 1; } और उसके बाद बस कुंजियों और मूल्यों को प्रिंट करें।
विंको वर्सालोविच 14

4

समाधान (mysql की तरह समूह)

grep -ioh "facebook\|xing\|linkedin\|googleplus" access-log.txt | sort | uniq -c | sort -n

परिणाम

3249  googleplus
4211 linkedin
5212 xing
7928 facebook

3

आप शायद हैश टेबल के रूप में फाइल सिस्टम का उपयोग कर सकते हैं। छद्म कोड निम्नानुसार है:

for every entry in the ip address file; do
  let addr denote the ip address;

  if file "addr" does not exist; then
    create file "addr";
    write a number "0" in the file;
  else 
    read the number from "addr";
    increase the number by 1 and write it back;
  fi
done

अंत में, आपको बस इतना करना है कि सभी फाइलों को ट्रेस करना है और उनमें फाइल के नाम और नंबर को प्रिंट करना है। वैकल्पिक रूप से, एक गिनती रखने के बजाय, आप हर बार फ़ाइल में एक स्थान या एक नई पंक्ति जोड़ सकते हैं, और अंत में बस बाइट्स में फ़ाइल का आकार देखें।


3

मुझे लगता है कि इस मामले में awk साहचर्य सरणी भी आसान है

$ awk '{count[$1]++}END{for(j in count) print j,count[j]}' ips.txt

यहाँ पोस्ट द्वारा एक समूह


येप, महान ऑक सॉल्यूशन, लेकिन ओके अभी मशीन पर avaialable नहीं था जो मैं यह कर रहा था।
जिज्सेन्स

1

अधिकांश अन्य समाधान डुप्लिकेट की गणना करते हैं। यदि आपको वास्तव में प्रमुख मूल्य जोड़े समूह बनाने की आवश्यकता है, तो यह प्रयास करें:

यहाँ मेरा उदाहरण डेटा है:

find . | xargs md5sum
fe4ab8e15432161f452e345ff30c68b0 a.txt
30c68b02161e15435ff52e34f4fe4ab8 b.txt
30c68b02161e15435ff52e34f4fe4ab8 c.txt
fe4ab8e15432161f452e345ff30c68b0 d.txt
fe4ab8e15432161f452e345ff30c68b0 e.txt

यह md5 चेकसम द्वारा समूहीकृत कुंजी मूल्य जोड़े को प्रिंट करेगा।

cat table.txt | awk '{print $1}' | sort | uniq  | xargs -i grep {} table.txt
30c68b02161e15435ff52e34f4fe4ab8 b.txt
30c68b02161e15435ff52e34f4fe4ab8 c.txt
fe4ab8e15432161f452e345ff30c68b0 a.txt
fe4ab8e15432161f452e345ff30c68b0 d.txt
fe4ab8e15432161f452e345ff30c68b0 e.txt

1

शुद्ध (कोई कांटा नहीं!)

एक तरीका है, एक का उपयोग कर कार्य । यह रास्ता बहुत जल्दी है क्योंकि कोई कांटा नहीं है! "

... जबकि की गुच्छा आईपी पतों रहने छोटे !

countIp () { 
    local -a _ips=(); local _a
    while IFS=. read -a _a ;do
        ((_ips[_a<<24|${_a[1]}<<16|${_a[2]}<<8|${_a[3]}]++))
    done
    for _a in ${!_ips[@]} ;do
        printf "%.16s %4d\n" \
          $(($_a>>24)).$(($_a>>16&255)).$(($_a>>8&255)).$(($_a&255)) ${_ips[_a]}
    done
}

नोट: IP पते को 32 बिट्स अहस्ताक्षरित पूर्णांक मान में परिवर्तित किया जाता है, जिसका उपयोग सरणी के लिए सूचकांक के रूप में किया जाता है । यह साधारण बैश सरणियों का उपयोग करता है , साहचर्य सरणी (जो अधिक महंगा है) नहीं!

time countIp < ip_addresses 
10.0.10.1    3
10.0.10.2    1
10.0.10.3    1
real    0m0.001s
user    0m0.004s
sys     0m0.000s

time sort ip_addresses | uniq -c
      3 10.0.10.1
      1 10.0.10.2
      1 10.0.10.3
real    0m0.010s
user    0m0.000s
sys     0m0.000s

मेरे मेजबान पर, ऐसा करने से कांटे का उपयोग करने की तुलना में बहुत तेज है, लगभग 1'000 पतों तक, लेकिन जब मैं 10'000 पतों की गिनती करने की कोशिश करूँगा, तो लगभग 1 सेकंड में लगभग 1 सेकंड का समय लेगा।


0

मैंने इसे इस तरह किया है:

perl -e 'while (<>) {chop; $h{$_}++;} for $k (keys %h) {print "$k $h{$k}\n";}' ip_addresses

लेकिन यूनीक आपके लिए काम कर सकता है।


जैसा कि मैंने बताया कि मूल पोस्ट पर्ल एक विकल्प नहीं है। मुझे पता है कि यह पर्ल में आसान है, उस के साथ कोई समस्या नहीं :-)
Zizzencs

0

मैं समझता हूं कि आप बैश में कुछ ढूंढ रहे हैं, लेकिन अगर कोई और अजगर में कुछ ढूंढ रहा है, तो आप इस पर विचार कर सकते हैं:

mySet = set()
for line in open("ip_address_file.txt"):
     line = line.rstrip()
     mySet.add(line)

जैसा कि सेट में मान डिफ़ॉल्ट रूप से अद्वितीय हैं और अजगर इस सामान पर बहुत अच्छा है, आप यहां कुछ जीत सकते हैं। मैंने कोड का परीक्षण नहीं किया है, इसलिए यह खराब हो सकता है, लेकिन यह आपको वहां मिल सकता है। और यदि आप घटनाओं को गिनना चाहते हैं, तो सेट के बजाय एक हुक का उपयोग करना आसान है।

संपादित करें: मैं एक घटिया पाठक हूं, इसलिए मैंने गलत उत्तर दिया। यहां एक तानाशाही के साथ एक स्निपेट है जो घटनाओं की गणना करेगा।

mydict = {}
for line in open("ip_address_file.txt"):
    line = line.rstrip()
    if line in mydict:
        mydict[line] += 1
    else:
        mydict[line] = 1

डिक्शनरी mydict में अब विशिष्ट IP की कुंजी और उनके मान के रूप में होने वाली राशि की एक सूची है।


यह कुछ भी नहीं गिनता। आपको एक तानाशाही चाहिए जो स्कोर बनाए रखे।

रवींद्र। प्रश्न का बुरा पढ़ना, क्षमा करें। मेरे पास मूल रूप से प्रत्येक आईपी पते की मात्रा को स्टोर करने के लिए एक तानाशाह का उपयोग करने के बारे में थोड़ा सा था, लेकिन इसे हटा दिया, क्योंकि, ठीक है, मैंने प्रश्न को बहुत अच्छी तरह से नहीं पढ़ा था। * ठीक से जागने की कोशिश करता है
wzzrd 16

2
वहाँ एक है itertools.groupby()जो sorted()ओपी पूछता है के साथ संयुक्त रूप से करता है।
jfs

यह अजगर में एक महान समाधान है, जो इस के लिए उपलब्ध नहीं था :-)
Zizzencs

-8

यदि आदेश महत्वपूर्ण नहीं है, तो क्रमबद्ध छोड़ा जा सकता है

uniq -c <source_file>

या

echo "$list" | uniq -c

यदि स्रोत सूची एक चर है


1
यूनीक मैन पेज से आगे स्पष्ट करने के लिए: नोट: 'यूनीक' बार-बार लाइनों का पता नहीं लगाता है जब तक कि वे आसन्न न हों। आप पहले इनपुट को सॉर्ट करना चाहते हैं, या 'यूनीक' के बिना 'सॉर्ट-यू' का उपयोग कर सकते हैं।
कनवर्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.