มีวิธีลบบรรทัดที่ซ้ำกันในไฟล์ใน Unix หรือไม่?
ฉันสามารถทำได้ด้วยsort -u
และuniq
คำสั่ง แต่ฉันต้องการใช้sed
หรือawk
. เป็นไปได้หรือไม่
มีวิธีลบบรรทัดที่ซ้ำกันในไฟล์ใน Unix หรือไม่?
ฉันสามารถทำได้ด้วยsort -u
และuniq
คำสั่ง แต่ฉันต้องการใช้sed
หรือawk
. เป็นไปได้หรือไม่
awk
แต่จะค่อนข้างใช้ทรัพยากรในไฟล์ขนาดใหญ่
คำตอบ:
awk '!seen[$0]++' file.txt
seen
เป็น Associative-array ที่ Awk จะส่งผ่านทุกบรรทัดของไฟล์ไป หากบรรทัดไม่อยู่ในอาร์เรย์seen[$0]
จะประเมินเป็นเท็จ !
เป็นผู้ประกอบการไม่เชิงตรรกะและจะกลับเท็จจริง Awk จะพิมพ์บรรทัดที่นิพจน์ประเมินว่าเป็นจริง การ++
เพิ่มขึ้นseen
เพื่อให้seen[$0] == 1
หลังจากครั้งแรกที่พบบรรทัดแล้วseen[$0] == 2
และอื่น ๆ
Awk ประเมินทุกอย่างยกเว้น0
และ""
(สตริงว่าง) เป็นจริง ถ้าเป็นเส้นที่ซ้ำกันจะอยู่ในseen
นั้น!seen[$0]
จะมีการประเมินเป็นเท็จและสายจะไม่ถูกเขียนขึ้นเพื่อการส่งออก
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
จากhttp://sed.sourceforge.net/sed1line.txt : (โปรดอย่าถามฉันว่ามันทำงานอย่างไร ;-))
# delete duplicate, consecutive lines from a file (emulates "uniq").
# First line in a set of duplicate lines is kept, rest are deleted.
sed '$!N; /^\(.*\)\n\1$/!P; D'
# delete duplicate, nonconsecutive lines from a file. Beware not to
# overflow the buffer size of the hold space, or else use GNU sed.
sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'
$!
ส่วนที่จำเป็น? ไม่sed 'N; /^\(.*\)\n\1$/!P; D'
ทำสิ่งเดียวกัน? ฉันไม่สามารถหาตัวอย่างที่ทั้งสองแตกต่างกันในเครื่องของฉันได้ (fwiw ฉันลองใช้บรรทัดว่างในตอนท้ายของทั้งสองเวอร์ชันและใช้ได้ทั้งคู่)
[ -~]
หมายถึงช่วงของอักขระ ASCII ตั้งแต่ 0x20 (ช่องว่าง) ถึง 0x7E (ทิลเดอ) สิ่งเหล่านี้ถือเป็นอักขระ ASCII ที่พิมพ์ได้ (หน้าที่เชื่อมโยงยังมี 0x7F / ลบ แต่ดูเหมือนจะไม่ถูกต้อง) นั่นทำให้โซลูชันเสียสำหรับทุกคนที่ไม่ได้ใช้ ASCII หรือใครก็ตามที่ใช้พูดว่าอักขระแท็บ .. ยิ่งพกพาได้[^\n]
ยิ่งมีอักขระมากขึ้น ... ทั้งหมดของพวกเขายกเว้นอันที่จริง
Perl one-liner คล้ายกับโซลูชัน awk ของ @ jonas:
perl -ne 'print if ! $x{$_}++' file
รูปแบบนี้ลบช่องว่างต่อท้ายก่อนเปรียบเทียบ:
perl -lne 's/\s*$//; print if ! $x{$_}++' file
รูปแบบนี้แก้ไขไฟล์ในตำแหน่ง:
perl -i -ne 'print if ! $x{$_}++' file
รูปแบบนี้แก้ไขไฟล์ในตำแหน่งและทำการสำรองข้อมูล file.bak
perl -i.bak -ne 'print if ! $x{$_}++' file
อีกวิธีหนึ่งในการใช้ Vim (เข้ากันได้กับ Vi) :
ลบบรรทัดที่ซ้ำกันและต่อเนื่องกันออกจากไฟล์:
vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq
ลบบรรทัดที่ซ้ำกันไม่ต่อเนื่องและไม่ว่างเปล่าออกจากไฟล์:
vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq
ซับเดียวที่ Andre Miller โพสต์ไว้ข้างต้นใช้งานได้ยกเว้น sed เวอร์ชันล่าสุดเมื่อไฟล์อินพุตลงท้ายด้วยบรรทัดว่างและไม่มีตัวอักษร บน Mac ของฉัน CPU ของฉันหมุน
ลูปไม่มีที่สิ้นสุดหากบรรทัดสุดท้ายว่างเปล่าและไม่มีตัวอักษร :
sed '$!N; /^\(.*\)\n\1$/!P; D'
ไม่ค้าง แต่คุณจะสูญเสียบรรทัดสุดท้าย
sed '$d;N; /^\(.*\)\n\1$/!P; D'
คำอธิบายอยู่ท้ายสุดของคำถามที่พบบ่อย sed :
ผู้ดูแล GNU sed รู้สึกว่าแม้จะมีปัญหาในการพกพา
แต่การเปลี่ยนคำสั่ง N เพื่อพิมพ์ (แทนที่จะ
ลบ) พื้นที่รูปแบบนั้นสอดคล้องกับสัญชาตญาณของคน ๆ หนึ่ง
เกี่ยวกับวิธีการที่คำสั่ง "ต่อท้ายบรรทัดถัดไป" ควรจะทำงาน
ข้อเท็จจริงอีกประการหนึ่งที่สนับสนุนการเปลี่ยนแปลงคือ "{N; command;}" จะ
ลบบรรทัดสุดท้ายหากไฟล์มีจำนวนบรรทัดคี่ แต่
พิมพ์บรรทัดสุดท้ายหากไฟล์มีจำนวนบรรทัดคู่ในการแปลงสคริปต์ที่ใช้พฤติกรรมเดิมของ N (การลบ
พื้นที่รูปแบบเมื่อถึง EOF) เป็นสคริปต์ที่เข้ากันได้กับ
sed ทุกเวอร์ชันให้เปลี่ยน "N;" ถึง "$ d; N;" .
$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n\1$/!P;D'
1
2
3
4
5
แนวคิดหลักคือ:
print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.
อธิบาย:
$!N;
ถ้าบรรทัดปัจจุบันไม่ได้เป็นบรรทัดสุดท้ายให้ใช้คำสั่งในการอ่านบรรทัดถัดลงN
pattern space
/^(.*)\n\1$/!P
: หากเนื้อหาของกระแสpattern space
สองรายการduplicate string
คั่นด้วย\n
ซึ่งหมายความว่าบรรทัดถัดไปคือsame
กับบรรทัดปัจจุบันเราจะไม่สามารถพิมพ์ตามแนวคิดหลักของเราได้ มิฉะนั้นซึ่งหมายความว่าบรรทัดปัจจุบันคือลักษณะสุดท้ายของบรรทัดต่อเนื่องที่ซ้ำกันทั้งหมดตอนนี้เราสามารถใช้P
คำสั่งเพื่อพิมพ์ตัวอักษรในpattern space
util ปัจจุบัน\n
( \n
พิมพ์ด้วย)D
: เราใช้D
คำสั่งเพื่อลบตัวอักษรในpattern space
util ปัจจุบัน\n
( \n
ลบด้วย) จากนั้นเนื้อหาของpattern space
คือบรรทัดถัดไปD
คำสั่งจะบังคับsed
ให้ข้ามไปที่FIRST
คำสั่ง$!N
แต่ไม่อ่านบรรทัดถัดไปจากไฟล์หรือสตรีมอินพุตมาตรฐาน$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n\1$/\1/;tloop;D'
1
2
3
4
5
แนวคิดหลักคือ:
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
อธิบาย:
:loop
คำสั่งตั้งlabel
ชื่อloop
.N
เพื่ออ่านบรรทัดถัดไปในไฟล์pattern space
.s/^(.*)\n\1$/\1/
เพื่อลบบรรทัดปัจจุบันหากบรรทัดถัดไปเหมือนกันกับบรรทัดปัจจุบันเราใช้s
คำสั่งเพื่อdelete
ดำเนินการs
คำสั่งสำเร็จแล้วให้ใช้tloop
command force sed
เพื่อข้ามไปยังlabel
ชื่อloop
ซึ่งจะทำลูปเดียวกันไปยังบรรทัดถัดไปโดยไม่มีบรรทัดต่อเนื่องที่ซ้ำกันของบรรทัดซึ่งคือlatest printed
; มิฉะนั้นให้ใช้D
คำสั่งไปdelete
ยังบรรทัดซึ่งเหมือนกันกับlatest-printed line
และบังคับsed
ให้ข้ามไปที่คำสั่งแรกซึ่งเป็นp
คำสั่งเนื้อหาของปัจจุบันpattern space
คือบรรทัดใหม่ถัดไปbusybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
สามารถทำได้โดยใช้awk
Below Line จะแสดงค่าที่ไม่ซ้ำกัน
awk file_name | uniq
คุณสามารถส่งออกค่าเฉพาะเหล่านี้ไปยังไฟล์ใหม่
awk file_name | uniq > uniq_file_name
ไฟล์ใหม่ uniq_file_name จะมีเฉพาะค่าที่ไม่ซ้ำกันไม่มีรายการที่ซ้ำกัน
uniq จะถูกหลอกด้วยการเว้นวรรคและแท็บต่อท้าย เพื่อเลียนแบบการเปรียบเทียบของมนุษย์ฉันกำลังตัดแต่งช่องว่างและแท็บต่อท้ายทั้งหมดก่อนทำการเปรียบเทียบ
ฉันคิดว่า $! N; ต้องการการจัดฟันแบบหยิกมิฉะนั้นจะดำเนินต่อไปและนั่นคือสาเหตุของการวนซ้ำที่ไม่สิ้นสุด
ฉันมี bash 5.0 และ sed 4.7 ใน Ubuntu 20.10 ซับในตัวที่สองใช้งานไม่ได้ในการจับคู่ชุดอักขระ
รูปแบบสามรูปแบบอันดับแรกกำจัดบรรทัดซ้ำที่อยู่ติดกันอันดับที่สองเพื่อกำจัดบรรทัดซ้ำทุกที่ที่เกิดขึ้นสามเพื่อกำจัดอินสแตนซ์ของบรรทัดสุดท้ายทั้งหมดในไฟล์
# First line in a set of duplicate lines is kept, rest are deleted.
# Emulate human eyes on trailing spaces and tabs by trimming those.
# Use after norepeat() to dedupe blank lines.
dedupe() {
sed -E '
$!{
N;
s/[ \t]+$//;
/^(.*)\n\1$/!P;
D;
}
';
}
# Delete duplicate, nonconsecutive lines from a file. Ignore blank
# lines. Trailing spaces and tabs are trimmed to humanize comparisons
# squeeze blank lines to one
norepeat() {
sed -n -E '
s/[ \t]+$//;
G;
/^(\n){2,}/d;
/^([^\n]+).*\n\1(\n|$)/d;
h;
P;
';
}
lastrepeat() {
sed -n -E '
s/[ \t]+$//;
/^$/{
H;
d;
};
G;
# delete previous repeated line if found
s/^([^\n]+)(.*)(\n\1(\n.*|$))/\1\2\4/;
# after searching for previous repeat, move tested last line to end
s/^([^\n]+)(\n)(.*)/\3\2\1/;
$!{
h;
d;
};
# squeeze blank lines to one
s/(\n){3,}/\n\n/g;
s/^\n//;
p;
';
}
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
ลบบรรทัดที่ซ้ำกันโดยใช้ awk
cat
uniq
uniq
เพียงอย่างเดียวก็เพียงพอแล้ว