मैं बैश में एक सरणी से अद्वितीय मूल्य कैसे प्राप्त कर सकता हूं?


93

मुझे यहाँ लगभग वही प्रश्न मिला है

मेरे पास एक सरणी है जिसमें शामिल है aa ab aa ac aa ad, आदि। अब मैं इस सरणी से सभी अद्वितीय तत्वों का चयन करना चाहता हूं। सोचा, यह उस अन्य प्रश्न के उल्लेख के साथ sort | uniqया साथ सरल होगा sort -u, लेकिन सरणी में कुछ भी नहीं बदला ... कोड है:

echo `echo "${ids[@]}" | sort | uniq`

मैं क्या गलत कर रहा हूं?

जवाबों:


131

थोड़ा हैकी, लेकिन यह करना चाहिए:

echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '

सरणी में वापस किए गए अद्वितीय परिणामों को सहेजने के लिए, सरणी असाइनमेंट करें :

sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

यदि आपका शेल हेरस्ट्रेस ( bashचाहिए) का समर्थन करता है , तो आप echoइसे बदलकर एक प्रक्रिया को समाप्त कर सकते हैं :

tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '

इनपुट:

ids=(aa ab aa ac aa ad)

आउटपुट:

aa ab ac ad

स्पष्टीकरण:

  • "${ids[@]}"- शेल सरणियों के साथ काम करने के लिए सिंटैक्स, चाहे वह echoएक हिस्से के रूप में इस्तेमाल किया गया हो या एक हेस्ट्रिंग। @भाग का अर्थ है "सरणी में सभी तत्वों"
  • tr ' ' '\n'- सभी रिक्त स्थान को न्यूलाइंस में बदलें। क्योंकि आपके सरणी को शेल द्वारा एक लाइन पर तत्वों के रूप में देखा जाता है, जिसे रिक्त स्थान द्वारा अलग किया जाता है; और क्योंकि सॉर्ट इनपुट को अलग लाइनों पर होने की उम्मीद करता है।
  • sort -u - केवल अद्वितीय तत्वों को सॉर्ट करें और बनाए रखें
  • tr '\n' ' ' - हमने पूर्व में वापस जोड़े गए नए समाचारों को रिक्त स्थान में परिवर्तित कर दिया।
  • $(...)- कमान प्रतिस्थापन
  • एक तरफ: करने tr ' ' '\n' <<< "${ids[@]}"का एक और अधिक कुशल तरीका है:echo "${ids[@]}" | tr ' ' '\n'

37
+1। एक सा tidier: uniq तत्वों को एक नए सरणी में संग्रहीत करें:uniq=($(printf "%s\n" "${ids[@]}" | sort -u)); echo "${uniq[@]}"
ग्लेन जैकमैन

@glennjackman ओह नीम! मुझे यह भी एहसास नहीं था कि आप printfइस तरह से इस्तेमाल कर सकते हैं (फॉर्मेट स्ट्रिंग्स से अधिक तर्क दें)
sampson-chen

4
+1 यह सुनिश्चित नहीं है कि यह एक अलग मामला है, लेकिन एक अलग आइटम को एक सरणी में वापस लाना अतिरिक्त कोष्ठक की आवश्यकता है जैसे sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')):। अतिरिक्त कोष्ठक के बिना यह एक स्ट्रिंग के रूप में दे रहा था।
१०:३१ पर १०

3
यदि आप तत्वों के क्रम को बदलना नहीं चाहते हैं, तो ... | uniq | ...इसके बजाय का उपयोग करें ... | sort -u | ...
जेसी चिशोल्म

2
@ जेसे, uniqकेवल लगातार डुप्लिकेट हटाता है । इस उत्तर में उदाहरण में, sorted_unique_idsमूल के समान समाप्त हो जाएगा ids। आदेश को संरक्षित करने के लिए, प्रयास करें ... | awk '!seen[$0]++'। यह भी देखें stackoverflow.com/questions/1444406/...
रोब कैनेडी

29

यदि आप बैश संस्करण 4 या उससे ऊपर (जो कि लिनक्स के किसी भी आधुनिक संस्करण में होना चाहिए) चला रहे हैं, तो आप मूल सरणी के प्रत्येक मान वाले एक नए साहचर्य सरणी को बनाकर बैश में अद्वितीय सरणी मान प्राप्त कर सकते हैं। कुछ इस तरह:

$ a=(aa ac aa ad "ac ad")
$ declare -A b
$ for i in "${a[@]}"; do b["$i"]=1; done
$ printf '%s\n' "${!b[@]}"
ac ad
ac
aa
ad

यह काम करता है क्योंकि किसी भी एरे (किसी भाषा में सहयोगी या पारंपरिक) में, प्रत्येक कुंजी केवल एक बार दिखाई दे सकती है। जब forलूप के दूसरे मूल्य पर आता aaहै a[2], तो यह ओवरराइट करता है b[aa]जो मूल रूप से इसके लिए सेट किया गया था a[0]

देशी बैश में चीजें करना पाइप और बाहरी उपकरणों का उपयोग करने की तुलना में तेज हो सकता है sortऔर uniq, हालांकि बड़े डेटासेट के लिए आप बेहतर प्रदर्शन देखेंगे यदि आप अधिक शक्तिशाली भाषा जैसे कि awk, python, आदि का उपयोग करते हैं।

यदि आप आश्वस्त महसूस कर रहे हैं, तो आप कई तर्कों के लिए अपने प्रारूप को रीसायकल करने की क्षमता forका उपयोग करके लूप से बच सकते हैं printf, हालांकि इसके लिए आवश्यकता होती है eval। (यदि आप इसके साथ ठीक हैं तो अभी पढ़ना बंद कर दें।)

$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )

इस समाधान की आवश्यकता evalहै कि शब्द बंटवारे से पहले सरणी मान निर्धारित किए जाते हैं। इसका मतलब है कि कमांड प्रतिस्थापन का आउटपुट कुंजी = मान जोड़े के एक सेट के बजाय एक शब्द माना जाता है

हालांकि यह एक सबशेल का उपयोग करता है, यह सरणी मान को संसाधित करने के लिए केवल बैश बिल्डिंस का उपयोग करता है। evalआलोचनात्मक नज़र से अपने उपयोग का मूल्यांकन करना सुनिश्चित करें । यदि आप 100% आश्वस्त नहीं हैं कि चेपर या ग्लेन जैकमैन या ग्रीकाट को आपके कोड के साथ कोई गलती नहीं मिलेगी, तो लूप के बजाय उपयोग करें।


त्रुटि पैदा करता है: अभिव्यक्ति पुनरावृत्ति स्तर पार हो गया
बेनुबर्ड

1
@Benubird - क्या आप शायद अपनी टर्मिनल सामग्री को पास्तिन कर सकते हैं? यह मेरे लिए पूरी तरह से काम करता है, इसलिए मेरा सबसे अच्छा अनुमान है कि आपको (1) एक टाइपो, (2) बैश का एक पुराना संस्करण (साहचर्य सरणियाँ v4 में जोड़ा गया), या (3) लौकिक पृष्ठभूमि का एक हास्यास्पद बड़ा प्रवाह है आपके पड़ोसी के तहखाने में क्वांटम ब्लैक होल के कारण विकिरण, आपके कंप्यूटर के भीतर संकेतों के साथ हस्तक्षेप पैदा करता है।
घटि

1
नहीं कर सकता है, एक है कि काम नहीं किया नहीं रखा। लेकिन, मैंने अभी-अभी तुम्हारा चलाने की कोशिश की और यह काम किया, इसलिए शायद लौकिक विकिरण की बात।
बेनुबर्ड

यह अनुमान लगाते हुए कि यह उत्तर bash v4 (साहचर्य सरणियों) का उपयोग करता है और यदि कोई bash v3 में कोशिश करता है तो यह काम नहीं करेगा (शायद @Benubird ने नहीं देखा)। बैश v3 अभी भी कई एनवीएस में डिफ़ॉल्ट है
ned

1
@ आनंद, बिंदु लिया। मैं देखता हूं कि मेरे अप-टू-डेट योसेमाइट मैकबुक का आधार में एक ही संस्करण है, हालांकि मैंने मैकपोर्ट से v4 स्थापित किया है। इस प्रश्न को "linux" टैग किया गया है, लेकिन मैंने आवश्यकता को इंगित करने के लिए अपना उत्तर अपडेट कर दिया है।
घोटी

18

मुझे लगता है कि यह पहले से ही उत्तर दिया गया था, लेकिन यह खोज परिणामों में बहुत अधिक दिखा, और यह किसी की मदद कर सकता है।

printf "%s\n" "${IDS[@]}" | sort -u

उदाहरण:

~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo  "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s\n" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>

1
सरणी को ठीक करने के लिए मुझे ऐसा करने के लिए मजबूर किया गया था: ids=(ab "a a" ac aa ad ac aa);IFS=$'\n' ids2=(`printf "%s\n" "${ids[@]}" |sort -u`)इसलिए मैंने IFS=$'\n'@gniourf_gniourf द्वारा सुझाव दिया
कुंभ राशि

मुझे भी बैकअप करना पड़ा और, कमांड के बाद, IFS मान को पुनर्स्थापित करें! या यह अन्य चीजों को गड़बड़ करता है ..
कुंभ राशि

@Jetse यह स्वीकृत उत्तर होना चाहिए क्योंकि यह केवल दो कमांड का उपयोग करता है, कोई लूप नहीं, कोई eval नहीं है और सबसे कॉम्पैक्ट संस्करण है।
mgutt

1
@AquariusPower सावधान, आप मूल रूप से कर रहे हैं: IFS=$'\n'; ids2=(...)चूंकि अस्थायी असाइनमेंट से पहले अस्थायी असाइनमेंट संभव नहीं है। इसके बजाय इस निर्माण का उपयोग करें IFS=$'\n' read -r -a ids2 <<<"$(printf "%s\n" "${ids[@]}" | sort -u)":।
यति

13

यदि आपके सरणी तत्वों में सफेद स्थान या कोई अन्य शेल विशेष वर्ण है (और क्या आप सुनिश्चित कर सकते हैं कि वे ऐसा न करें?) तो उन सभी को पकड़ने के लिए (और आपको हमेशा ऐसा करना चाहिए) अपने सरणी को दोहरे उद्धरण चिह्नों में व्यक्त करें! उदा "${a[@]}"। बैश शाब्दिक रूप से "एक अलग तर्क में प्रत्येक सरणी तत्व" के रूप में इसकी व्याख्या करेगा । बस के भीतर यह बस हमेशा काम करता है, हमेशा।

फिर, एक सॉर्ट किया गया (और अद्वितीय) सरणी प्राप्त करने के लिए, हमें इसे एक स्वरूप सॉर्ट में बदलना होगा जो समझता है और इसे वापस बैश एरे तत्वों में परिवर्तित करने में सक्षम है। यह मेरे साथ आया सबसे अच्छा है:

eval a=($(printf "%q\n" "${a[@]}" | sort -u))

दुर्भाग्य से, यह खाली सरणी के विशेष मामले में विफल रहता है, खाली सरणी को 1 खाली तत्व के सरणी में बदल देता है (क्योंकि प्रिंटफ में 0 तर्क थे लेकिन फिर भी प्रिंट होते हैं जैसे कि इसमें एक खाली तर्क था - स्पष्टीकरण देखें)। तो आपको यह पकड़ना होगा कि अगर एक या कुछ में।

स्पष्टीकरण: प्रिंटफ़ के लिए% q प्रारूप "शेल बच जाता है" मुद्रित तर्क, बस इस तरह के रूप में बैश eval जैसी किसी चीज़ में पुनर्प्राप्त कर सकता है! क्योंकि प्रत्येक तत्व मुद्रित है, यह स्वयं की रेखा पर बच गया है, तत्वों के बीच एकमात्र विभाजक न्यूलाइन है, और सरणी असाइनमेंट प्रत्येक लाइन को एक तत्व के रूप में लेता है, बच गए मूल्यों को शाब्दिक पाठ में पार्स करता है।

जैसे

> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''

सरणी में वापस जाने वाले प्रत्येक मूल्य से बचने के लिए पट्टी को निकालना आवश्यक है।


यह एकमात्र कोड है जो मेरे लिए काम करता है क्योंकि मेरे सरणी में रिक्त स्थान थे। द% ख करत% ह। धन्यवाद :)
सोमैया कुम्बरा

और अगर आप तत्वों के क्रम को बदलना नहीं चाहते हैं, तो uniqइसके बजाय का उपयोग करें sort -u
जेसी चिशोल्म

ध्यान दें कि uniqअनसोल्ड सूचियों पर ठीक से काम नहीं करता है, इसलिए इसे हमेशा संयोजन में उपयोग किया जाना चाहिए sort
जीन पॉल

एक अनसुलझी सूची पर uniq लगातार डुप्लिकेट को हटा देगा । यह समान सूची तत्वों को कुछ और इनबेटीइन द्वारा अलग नहीं करेगा। uniq अपेक्षित डेटा और मूल आदेश को बनाए रखने की इच्छा के आधार पर पर्याप्त उपयोगी हो सकता है।
vontrapp

10

'सॉर्ट' का उपयोग फॉर-लूप के आउटपुट के लिए किया जा सकता है:

for i in ${ids[@]}; do echo $i; done | sort

और "-u" के साथ डुप्लिकेट को समाप्त करें:

for i in ${ids[@]}; do echo $i; done | sort -u

अंत में आप केवल अद्वितीय तत्वों के साथ अपने सरणी को अधिलेखित कर सकते हैं:

ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )

और अगर आप जो बचा है उसका क्रम बदलना नहीं चाहते हैं, तो आपके पास नहीं है:ids=( `for i in ${ids[@]}; do echo $i; done | uniq` )
जेसी चिशोल्म

3

यह भी आदेश की रक्षा करेगा:

echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'

और अद्वितीय मानों के साथ मूल सरणी को संशोधित करने के लिए:

ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'))

उपयोग न करें uniq। इसे क्रमबद्ध करने की आवश्यकता होती है, जहां awk नहीं होता है, और इस उत्तर का आशय इनपुट के अनसोल्ड होने पर ऑर्डर को संरक्षित करना है।
bukzor

2

अद्वितीय मानों से मिलकर एक नया सरणी बनाने के लिए, सुनिश्चित करें कि आपका सरणी खाली नहीं है फिर निम्न में से कोई एक कार्य करें:

डुप्लिकेट प्रविष्टियाँ निकालें (छँटाई के साथ)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)

डुप्लिकेट प्रविष्टियाँ निकालें (बिना छांटे)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')

चेतावनी: ऐसा कुछ करने की कोशिश न करें NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) )। यह रिक्त स्थान पर टूट जाएगा।


डुप्लिकेट प्रविष्टियों को हटाएं (बिना छांटे) परिवर्तन की तरह ही (छंटाई के साथ) sort -uहै uniq
जेसी चिशोल्म

@JesseChisholm uniqकेवल डुप्लिकेट लाइनों का विलय करता है जो आसन्न हैं, इसलिए यह वैसा नहीं है awk '!x[$0]++'
छह

@JesseChisholm कृपया भ्रामक टिप्पणी को हटाने के लिए।
ब्यूकोर

2

बिल्ली नंबर

1 2 3 4 4 3 2 5 6

कॉलम में प्रिंट लाइन: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}'

1
2
3
4
4
3
2
5
6

डुप्लिकेट रिकॉर्ड खोजें: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++'

4
3
2

डुप्लिकेट रिकॉर्ड बदलें: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++'

1
2
3
4
5
6

केवल यूनीक रिकॉर्ड खोजें: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}

1
5
6

1

मूल आदेश को खोए बिना:

uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[$0]++' | tr '\n' ' '))

1

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

declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do 
  uniqs["${f}"]=""
done

for thing in "${!uniqs[@]}"; do
  echo "${thing}"
done

यह आउटपुट करेगा

bar
foo
bar none

मैंने अभी देखा कि यह आवश्यक रूप से @ghotis के उत्तर के समान है, सिवाय इसके कि समाधान सूची आइटमों को खाते में नहीं लेता है।
RLN

अच्छी बात। मैंने अपने समाधान में उद्धरण जोड़े हैं इसलिए अब यह रिक्त स्थान को संभालता है मैंने मूल रूप से इसे केवल प्रश्न में नमूना डेटा को संभालने के लिए लिखा था, लेकिन इस तरह आकस्मिकताओं को कवर करना हमेशा अच्छा होता है। सलाह के लिये धन्यवाद।
घोटी

1

एम्बेडेड व्हाट्सएप से निपटने के लिए एक अन्य विकल्प, के साथ अशक्त करना printf, अलग करना है sort, फिर एक सरणी में इसे वापस पैक करने के लिए एक लूप का उपयोग करें:

input=(a b c "$(printf "d\ne")" b c "$(printf "d\ne")")
output=()

while read -rd $'' element
do 
  output+=("$element")
done < <(printf "%s\0" "${input[@]}" | sort -uz)

इसके अंत में, inputऔर outputवांछित मान सम्‍मिलित करें (बशर्ते आदेश महत्वपूर्ण न हो):

$ printf "%q\n" "${input[@]}"
a
b
c
$'d\ne'
b
c
$'d\ne'

$ printf "%q\n" "${output[@]}"
a
b
c
$'d\ne'

1

इस भिन्नता के बारे में कैसे?

printf '%s\n' "${ids[@]}" | sort -u

और फिर sorted_arr=($(printf '%s\n' "${ids[@]}" | sort -u)
शैवाल

0

फ़ाइल में पहले कॉलम के लिए uniq मान प्राप्त करने के लिए इसे आज़माएं

awk -F, '{a[$1];}END{for (i in a)print i;}'

-3
# Read a file into variable
lines=$(cat /path/to/my/file)

# Go through each line the file put in the variable, and assign it a variable called $line
for line in $lines; do
  # Print the line
  echo $line
# End the loop, then sort it (add -u to have unique lines)
done | sort -u
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.