จุดตัดของสองรายการใน Bash


163

ฉันพยายามเขียนสคริปต์ง่ายๆที่จะแสดงเนื้อหาที่พบในสองรายการ เพื่อให้ง่ายขึ้นลองใช้ ls เป็นตัวอย่าง ลองนึกภาพ "หนึ่ง" และ "สอง" เป็นไดเรกทอรี

one = `ls one`
two = `ls two`
สี่แยก $ หนึ่ง $ สอง

ฉันยังค่อนข้างเขียวอยู่ในทุบตีดังนั้นอย่าลังเลที่จะแก้ไขวิธีที่ฉันทำเช่นนี้ ฉันแค่ต้องการคำสั่งที่จะพิมพ์ไฟล์ทั้งหมดใน "หนึ่ง" และ "สอง" พวกเขาจะต้องมีอยู่ทั้งคู่ คุณอาจเรียกสิ่งนี้ว่า "ทางแยก" ระหว่าง "หนึ่ง" และ "สอง"


ไม่มีอะไรที่นี่จริงตอบคำถาม: วิธีการตัดสองตัวแปรในสคริปต์ Bash
jameshfisher

ดูเหมือนว่าคำถามใหม่ในความคิดของฉันคำถามนั้นตอบได้อย่างชัดเจนที่นี่
Jean-Christophe Meillaud

แนวทางที่มีประโยชน์มากกว่านี้อยู่ในstackoverflow.com/questions/2312762/…ที่
Tripleee

คำตอบ:


285
comm -12  <(ls 1) <(ls 2)

37
ไม่อยากเชื่อเลยว่าcommวันนี้ฉันไม่รู้เลย นี่เพิ่งทำให้ทั้งสัปดาห์ของฉัน :)
23414 Darragh Enright

22
commต้องการอินพุตที่จะเรียงลำดับ ในกรณีนี้lsเรียงลำดับผลลัพธ์โดยอัตโนมัติ แต่การใช้งานอื่นอาจจำเป็นต้องทำเช่นนี้:comm -12 <(some-command | sort) <(some-other-command | sort)
Alexander Bird

11
อย่าใช้เอาต์พุตของทุกสิ่ง ls เป็นเครื่องมือสำหรับการดูข้อมูลเมตาของไดเรกทอรีแบบโต้ตอบ ความพยายามใด ๆ ในการแยกวิเคราะห์เอาต์พุตของ ls ด้วยโค้ดจะใช้งานไม่ได้ Globs นั้นง่ายและถูกต้องมากขึ้น: '' สำหรับไฟล์ใน * .txt '' อ่านmywiki.wooledge.org/ParsingLs
Rany Albeg Wein

2
ฉันเพิ่งใช้สิ่งนี้ในความพยายามที่จะค้นหาpublicวิธีการที่error()ได้รับจากคุณลักษณะร่วมกับgit grepและมันยอดเยี่ยมมาก! ฉันวิ่ง$ comm -12 <(git grep -il "\$this->error(" -- "*.php") <(git grep -il "Dash_Api_Json_Response" -- "*.php")และโชคดีที่ฉันได้ชื่อไฟล์ที่มีลักษณะเฉพาะเท่านั้น
localheinz

3
นี่คือเฮฮา ฉันพยายามทำสิ่งบ้า ๆ บอ ๆ ด้วย awk
Rolf

55

วิธีแก้ไขด้วย comm

commยอดเยี่ยม แต่ต้องทำงานกับรายการที่เรียงลำดับ และที่นี่เราโชคดีที่ใช้lsจากlsหน้า Bash man

เรียงลำดับรายการตามตัวอักษรหากไม่มี -cftuSUX หรือ --sort

comm -12  <(ls one) <(ls two)

ทางเลือกด้วย sort

จุดตัดของสองรายการ:

sort <(ls one) <(ls two) | uniq -d

ความแตกต่างสมมาตรของสองรายการ:

sort <(ls one) <(ls two) | uniq -u

โบนัส

เล่นกับมัน ;)

cd $(mktemp -d) && mkdir {one,two} && touch {one,two}/file_{1,2}{0..9} && touch two/file_3{0..9}

2
แทนการเติมเต็มผมคิดว่านั่นคือสิ่งที่มักจะเรียกว่าแตกต่างสมมาตร
แอนดรูลาซารัส

29

ใช้commคำสั่ง:

ls one | sort > /tmp/one_list
ls two | sort > /tmp/two_list
comm -12 /tmp/one_list /tmp/two_list

"sort" ไม่จำเป็นจริงๆ แต่ฉันมักจะรวมไว้ก่อนที่จะใช้ "comm" ในกรณี


5
เป็นการดีที่จะรวมไว้เนื่องจากไม่จำเป็นต้องเรียงลำดับและเขาใช้ ls เป็นตัวอย่างเท่านั้น
Thor84no

3

ทางเลือกที่มีประสิทธิภาพ (น้อยกว่า comm):

cat <(ls 1 | sort -u) <(ls 2 | sort -u) | uniq -d

1
หากคุณกำลังใช้ Debian ของ / bin / เส้นประหรือบางส่วนที่ไม่ใช่เปลือกทุบตีอื่น ๆ (ls 1; ls 2) | sort -u | uniq -dในสคริปต์ของคุณคุณสามารถส่งออกคำสั่งโซ่ใช้วงเล็บ:
ไนโตรเจน

1
@ MikaëlMayerคุณควรตั้งค่าสถานะของบุคคลที่คุณตอบกลับมิฉะนั้นจะถือว่าคุณหมายถึงฉัน
Benubird

@nitrogen MikaëlMayerถูกต้อง - chainging sort -u | uniq -dไม่ทำอะไรเลยเนื่องจากการเรียงลำดับได้ลบรายการที่ซ้ำกันก่อนที่ uniq จะเริ่มค้นหาพวกเขา ฉันคิดว่าคุณไม่เข้าใจว่าคำสั่งของฉันกำลังทำอะไรอยู่
Benubird

@Benubird ฉันไม่สามารถรับคำสั่งของคุณcat <(ls 1 | sort -u) <(ls 2 | sort -u) | uniq -dเพื่อส่งออกสิ่งใด คำสั่งของฉันควรอ่าน(ls 1; ls 2) | sort | uniq -dโดยไม่ใช้-uเพื่อแสดงจุดแยกรายการ @ MikaëlMayerพูดถูกว่าคำสั่งดั้งเดิมของฉันมันพัง
ไนโตรเจน

@nitrogen เหตุผลที่ฉันใช้แมวเป็นเพราะฉันอยากให้เรื่องนี้จะเป็นทางออกที่ generalizable เพื่อให้คุณสามารถแทนที่กับสิ่งอื่นเช่นls findโซลูชันของคุณไม่อนุญาตให้ทำเช่นนี้เพราะหากคำสั่งใดคำสั่งหนึ่งส่งคืนสองบรรทัดเหมือนกันระบบจะเลือกสำเนาซ้ำ Mine ทำงานได้แม้ว่าผู้ใช้ต้องการทำls 1/*และเปรียบเทียบไฟล์ทั้งหมดในไดเรกทอรีย่อย มิฉะนั้นก็ใช้งานได้เช่นกัน มันเป็นไปได้ที่ฉันจะใช้วิธีทุบตี
Benubird

2

เข้าร่วมเป็นอีกตัวเลือกที่ดีขึ้นอยู่กับอินพุตและเอาต์พุตที่ต้องการ

join -j1 -a1 <(ls 1) <(ls 2)

-1

มีอีก Stackoverflow คำถาม "Array intersection in bash" ซึ่งถูกทำเครื่องหมายว่าซ้ำกัน ในความคิดของฉันมันไม่เหมือนกันในขณะที่คำถามพูดคุยเกี่ยวกับการเปรียบเทียบสองทุบตีอาร์เรย์ในขณะที่คำถามนี้มุ่งเน้นไปที่ไฟล์ทุบตี คำตอบเดียวกับคำถามอื่นซึ่งปิดตอนนี้มีดังนี้:

# List1=( 0 1 2 3 4   6 7 8 9 10 11 12)
# List2=(   1 2 3   5 6   8 9    11 )
# List3=($(comm -12 <(echo ${List1[*]}| tr " " "\n"| sort) <(echo ${List2[*]} | tr " " "\n"| sort)| sort -g))
# echo ${List3[*]}
1 2 3 6 8 9 11

ยูทิลิตีคอมมิชชันทำเรียงตัวอักษรและตัวเลขในขณะที่ "Array intersection in bash" ตอบให้ใช้ตัวเลข ดังนั้นการใช้ "sort" และ "sort -g"

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