เลือกค่าที่ไม่ซ้ำกันหรือแตกต่างจากรายการในเชลล์สคริปต์ UNIX


238

ฉันมีสคริปต์ ksh ที่คืนค่ารายการจำนวนมากบรรทัดใหม่คั่นและฉันต้องการเห็นเฉพาะค่าที่ไม่ซ้ำกัน / แตกต่างกัน เป็นไปได้ที่จะทำเช่นนี้?

ตัวอย่างเช่นสมมติว่าเอาต์พุตของฉันคือคำต่อท้ายไฟล์ในไดเรกทอรี:

tar
gz
java
gz
java
tar
class
class

ฉันต้องการดูรายการที่ชอบ:

tar
gz
java
class

คำตอบ:


432

คุณอาจต้องการดูuniqและsortแอปพลิเคชัน

./yourscript.ksh | จัดเรียง | UNIQ

(FYI, ใช่การเรียงลำดับเป็นสิ่งจำเป็นในบรรทัดคำสั่งนี้จะตัดuniqเฉพาะบรรทัดที่ซ้ำกันซึ่งอยู่ติดกันทันที)

แก้ไข:

ตรงกันข้ามกับสิ่งที่Aaron Digullaโพสต์เกี่ยวกับuniqตัวเลือกบรรทัดคำสั่ง:

รับอินพุตต่อไปนี้:

ชั้น
โถ
โถ
โถ
ถัง
ถัง
ชวา

uniq จะส่งออกทุกบรรทัดตรงครั้งเดียว:

ชั้น
โถ
ถัง
ชวา

uniq -d จะเอาท์พุททุกบรรทัดที่ปรากฏมากกว่าหนึ่งครั้งและมันจะพิมพ์ครั้งเดียว:

โถ
ถัง

uniq -u จะส่งออกทุกบรรทัดที่ปรากฏอย่างแน่นอนครั้งเดียวและจะพิมพ์ออกมาหนึ่งครั้ง:

ชั้น
ชวา

2
เพียงแค่ FYI สำหรับผู้มาที่หลัง: @ คำตอบของ AaronDigulla นั้นได้รับการแก้ไขแล้ว
mklement0

2
จุดที่ดีมาก ๆ `จำเป็นต้องมีการเรียงลำดับในบรรทัดคำสั่งนี้ uniq เพียงดึงบรรทัดที่ซ้ำกันที่อยู่ติดกันซึ่งฉันเพิ่งได้เรียนรู้ !!
HattrickNZ

4
GNU sortมี-uรุ่นสำหรับให้ค่าที่ไม่เหมือนใครด้วย
Arthur2e5

ฉันคิดว่าuniqตะเข็บในการประมวลผลเพียงเส้นที่อยู่ติดกัน (อย่างน้อยค่าเริ่มต้น) ความหมายหนึ่งอาจป้อนข้อมูลก่อนที่จะให้อาหารsort uniq
Stphane

85
./script.sh | sort -u

นี่เป็นคำตอบเดียวกับคำตอบของ monoxide แต่ค่อนข้างสั้นกว่า


6
คุณกำลังถ่อมตัว: โซลูชันของคุณจะทำงานได้ดีขึ้น (อาจสังเกตได้เฉพาะกับชุดข้อมูลขนาดใหญ่)
mklement0

ฉันคิดว่ามันควรจะมีประสิทธิภาพมากกว่า... | sort | uniqเพราะมันถูกแสดงในนัดเดียว
Adrian Antunez

10

สำหรับชุดข้อมูลขนาดใหญ่ที่การเรียงลำดับอาจไม่ต้องการคุณสามารถใช้สคริปต์ perl ต่อไปนี้:

./yourscript.ksh | perl -ne 'if (!defined $x{$_}) { print $_; $x{$_} = 1; }'

นี่เป็นเพียงแค่จดจำทุกบรรทัดเอาท์พุทเพื่อที่จะไม่เอาท์พุทอีกครั้ง

มันมีข้อได้เปรียบเหนือsort | uniqวิธีแก้ปัญหาโดยที่ไม่ต้องเรียงลำดับล่วงหน้า


2
โปรดทราบว่าการเรียงลำดับของไฟล์ที่มีขนาดใหญ่มากไม่ใช่ปัญหาต่อการเรียงลำดับ สามารถเรียงลำดับไฟล์ที่มีขนาดใหญ่กว่า RAM + swap ที่มีอยู่ Perl, OTOH จะล้มเหลวหากมีการทำซ้ำเพียงเล็กน้อย
Aaron Digulla

1
ใช่มันเป็นการแลกเปลี่ยนที่ขึ้นอยู่กับข้อมูลที่คาดหวัง Perl จะดีกว่าสำหรับชุดข้อมูลขนาดใหญ่ที่มีการทำซ้ำหลายรายการ (ไม่จำเป็นต้องใช้ที่เก็บข้อมูลบนดิสก์) ชุดข้อมูลขนาดใหญ่ที่มีข้อมูลซ้ำซ้อนน้อยควรใช้การเรียงลำดับ (และที่เก็บข้อมูลดิสก์) ชุดข้อมูลขนาดเล็กสามารถใช้ได้ โดยส่วนตัวแล้วฉันจะลอง Perl ก่อนสลับไปที่การเรียงลำดับถ้ามันล้มเหลว
paxdiablo

เนื่องจากการเรียงลำดับจะให้ประโยชน์แก่คุณหากมีการสลับไปยังดิสก์
paxdiablo

5
นี่เป็นสิ่งที่ยอดเยี่ยมเมื่อฉันต้องการให้เกิดขึ้นครั้งแรกของทุกบรรทัด การเรียงลำดับจะทำลายสิ่งนั้น
Bluu

10

ด้วยzshคุณสามารถทำได้:

% cat infile 
tar
more than one word
gz
java
gz
java
tar
class
class
zsh-5.0.0[t]% print -l "${(fu)$(<infile)}"
tar
more than one word
gz
java
class

หรือคุณสามารถใช้ AWK:

% awk '!_[$0]++' infile    
tar
more than one word
gz
java
class

2
โซลูชันที่ชาญฉลาดที่ไม่เกี่ยวข้องกับการเรียงลำดับอินพุต Caveats: awkโซลูชันที่ชาญฉลาด แต่มีความลับ(ดูstackoverflow.com/a/21200722/45375สำหรับคำอธิบาย) จะทำงานกับไฟล์ขนาดใหญ่ตราบใดที่จำนวนบรรทัดที่ไม่ซ้ำกันมีขนาดเล็กพอ (เนื่องจากบรรทัดที่ไม่ซ้ำกันจะถูกเก็บไว้ในหน่วยความจำ ) zshแก้ปัญหาการอ่านไฟล์ทั้งหมดในหน่วยความจำแรกซึ่งอาจจะไม่ได้เป็นตัวเลือกที่มีไฟล์ขนาดใหญ่ นอกจากนี้ตามที่เขียนจะมีการจัดการเฉพาะบรรทัดที่ไม่มีช่องว่างที่ฝังอยู่อย่างถูกต้อง เพื่อแก้ไขปัญหานี้ใช้IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}แทน
mklement0

แก้ไข. หรือ:(IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}")
Dimitre Radoulov

1
ขอบคุณที่ง่ายกว่า (สมมติว่าคุณไม่จำเป็นต้องตั้งค่าตัวแปรที่จำเป็นนอก subshell) ฉันอยากรู้ว่าเมื่อไหร่ที่คุณต้องการ[@]คำต่อท้ายเพื่ออ้างอิงองค์ประกอบทั้งหมดของอาเรย์ - ดูเหมือนว่า - อย่างน้อยเป็นเวอร์ชัน 5 - มันใช้งานได้โดยปราศจากมัน หรือคุณเพิ่งเพิ่มเพื่อความชัดเจน?
mklement0

1
@ mklement0 คุณพูดถูก! ฉันไม่คิดว่าเมื่อฉันเขียนโพสต์ ที่จริงแล้วน่าจะเพียงพอ:print -l "${(fu)$(<infile)}"
Dimitre Radoulov

1
เยี่ยมมากขอบคุณสำหรับการอัปเดตโพสต์ของคุณฉันใช้เสรีภาพในการแก้ไขawkเอาต์พุตตัวอย่างด้วย
mklement0

9

พวกเขาผ่านท่อและsort uniqสิ่งนี้จะลบรายการที่ซ้ำกันทั้งหมด

uniq -dให้ซ้ำuniq -uเท่านั้นให้เฉพาะที่ไม่ซ้ำกัน (แถบซ้ำกัน)


ต้องเรียงลำดับแรกโดยลักษณะของมัน
brabster

1
ใช่คุณทำ หรือแม่นยำยิ่งขึ้นคุณจะต้องจัดกลุ่มบรรทัดที่ซ้ำกันทั้งหมดเข้าด้วยกัน การเรียงลำดับไม่นี้โดยความหมายแม้ว่า;)
แมทธิว Scharley

นอกจากนี้uniq -uไม่ได้เป็นพฤติกรรมเริ่มต้น (ดูแก้ไขในคำตอบของฉันสำหรับรายละเอียด)
แมทธิว Scharley

7

ด้วย AWK คุณสามารถทำได้ฉันพบว่าเร็วกว่าการจัดเรียง

 ./yourscript.ksh | awk '!a[$0]++'

นั่นเป็นวิธีที่ฉันชอบที่สุดในการทำงานขอบคุณมาก! โดยเฉพาะอย่างยิ่งสำหรับไฟล์ขนาดใหญ่การเรียงลำดับ | uniq-solution อาจไม่ใช่สิ่งที่คุณต้องการ
Schmitzi

1

ไม่ซ้ำกันตามที่ร้องขอ (แต่ไม่ได้จัดเรียง);
ใช้ทรัพยากรระบบน้อยลงสำหรับองค์ประกอบน้อยกว่า 70 รายการ (ตามเวลาที่ทดสอบ)
เขียนเพื่อรับอินพุตจาก stdin
(หรือแก้ไขและรวมไว้ในสคริปต์อื่น):
(Bash)

bag2set () {
    # Reduce a_bag to a_set.
    local -i i j n=${#a_bag[@]}
    for ((i=0; i < n; i++)); do
        if [[ -n ${a_bag[i]} ]]; then
            a_set[i]=${a_bag[i]}
            a_bag[i]=$'\0'
            for ((j=i+1; j < n; j++)); do
                [[ ${a_set[i]} == ${a_bag[j]} ]] && a_bag[j]=$'\0'
            done
        fi
    done
}
declare -a a_bag=() a_set=()
stdin="$(</dev/stdin)"
declare -i i=0
for e in $stdin; do
    a_bag[i]=$e
    i=$i+1
done
bag2set
echo "${a_set[@]}"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.