ฉันต้องการสับเปลี่ยนบรรทัดไฟล์ข้อความแบบสุ่มและสร้างไฟล์ใหม่ ไฟล์อาจมีหลายพันบรรทัด
ฉันจะทำอย่างนั้นด้วยcat
, awk
, cut
etc?
ฉันต้องการสับเปลี่ยนบรรทัดไฟล์ข้อความแบบสุ่มและสร้างไฟล์ใหม่ ไฟล์อาจมีหลายพันบรรทัด
ฉันจะทำอย่างนั้นด้วยcat
, awk
, cut
etc?
คำตอบ:
shuf
คุณสามารถใช้ อย่างน้อยในบางระบบ (ดูเหมือนไม่มีใน POSIX)
ในฐานะที่เป็น jleedev ชี้ให้เห็น: sort -R
อาจเป็นตัวเลือก อย่างน้อยในบางระบบ ดีคุณจะได้รับภาพ มันได้รับการชี้ให้เห็นว่าsort -R
ไม่ได้โดดสับ แต่แทนที่จะเรียงลำดับรายการตามค่าแฮชของพวกเขา
[หมายเหตุจากบรรณาธิการ: sort -R
เกือบจะเป็นแบบสับยกเว้นปุ่มที่เรียงลำดับ / เรียงลำดับที่ซ้ำกันจะอยู่ติดกันเสมอ กล่าวอีกนัยหนึ่ง: เฉพาะกับบรรทัด / คีย์อินพุตที่ไม่ซ้ำกันเท่านั้นจึงเป็นการสลับที่แท้จริง แม้ว่าจะเป็นความจริงที่ว่าลำดับเอาต์พุตจะถูกกำหนดโดยค่าแฮชการสุ่มมาจากการเลือกฟังก์ชั่นแฮชแบบสุ่ม- ดูด้วยตนเอง ]
shuf
และsort -R
แตกต่างกันเล็กน้อยเพราะsort -R
สุ่มสั่งองค์ประกอบตามแฮชของพวกเขาซึ่งก็คือsort -R
จะทำให้องค์ประกอบที่ซ้ำกันเข้าด้วยกันในขณะที่shuf
สับองค์ประกอบทั้งหมดแบบสุ่ม
brew install coreutils
จากนั้นใช้gshuf ...
(:
sort -R
และshuf
ควรถูกมองว่าแตกต่างอย่างสิ้นเชิง sort -R
ไม่แน่นอน หากคุณโทรหาสองครั้งในเวลาที่ต่างกันในอินพุตเดียวกันคุณจะได้รับคำตอบเดียวกัน shuf
ในทางกลับกันจะสร้างเอาต์พุตแบบสุ่มดังนั้นจึงมีแนวโน้มที่จะให้เอาต์พุตที่แตกต่างกันในอินพุตเดียวกัน
Perl one-liner จะเป็นวิธีแก้ปัญหาแบบง่าย ๆ ของ Maxim
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
\n
; ใช่ว่า\n
จะต้องนำเสนอ - และมันมักจะเป็น - มิฉะนั้นคุณจะได้รับสิ่งที่คุณอธิบาย
<STDIN>
ด้วย<>
ดังนั้นโซลูชันจึงทำงานกับอินพุตจากไฟล์ด้วย
คำตอบนี้เติมเต็มคำตอบที่มีอยู่มากมายด้วยวิธีต่อไปนี้:
คำตอบที่มีอยู่ถูกบรรจุในฟังก์ชันเชลล์ที่ยืดหยุ่น :
stdin
ป้อนข้อมูลเข้าเท่านั้นแต่ยังมีอาร์กิวเมนต์ชื่อไฟล์หรือSIGPIPE
ในลักษณะปกติ (การยกเลิกแบบเงียบด้วยรหัสทางออก141
) ซึ่งแตกต่างจากเสียงดัง head
นี้เป็นสิ่งสำคัญเมื่อท่อส่งออกฟังก์ชั่นกับท่อที่ปิดให้บริการในช่วงต้นเช่นเมื่อท่อไปเปรียบเทียบประสิทธิภาพจะทำ
awk
, sort
และcut
ดัดแปลงมาจากคำตอบของตัวเองของ OP :shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "$@" |
sort -k1,1n | cut -d ' ' -f2-; }
shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@"; }
shuf() { python -c '
import sys, random, fileinput; from signal import signal, SIGPIPE, SIG_DFL;
signal(SIGPIPE, SIG_DFL); lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write("".join(lines))
' "$@"; }
ดูส่วนด้านล่างสำหรับฟังก์ชั่นเวอร์ชั่นWindows
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
puts ARGF.readlines.shuffle' "$@"; }
การเปรียบเทียบประสิทธิภาพ:
หมายเหตุ: ตัวเลขเหล่านี้ได้รับใน iMac ปลายปี 2012 กับ 3.2 GHz Intel Core i5 และ Fusion Drive โดยใช้ OSX 10.10.3 ในขณะที่การกำหนดเวลาจะแตกต่างกันไปตามระบบปฏิบัติการที่ใช้รายละเอียดเครื่องawk
การใช้งาน (เช่นawk
รุ่นBSD ที่ใช้บน OSX มักจะช้ากว่า GNU awk
และโดยเฉพาะอย่างยิ่งmawk
) สิ่งนี้ควรให้ความรู้สึกทั่วไปของประสิทธิภาพที่สัมพันธ์กัน
ป้อนข้อมูลไฟล์เป็นไฟล์ 1 ล้านเส้นseq -f 'line %.0f' 1000000
ผลิตด้วย
เวลาจะถูกเรียงตามลำดับจากน้อยไปหามาก (เร็วที่สุดก่อน):
shuf
0.090s
0.289s
0.589s
1.342s
ด้วย Python 2.7.6; 2.407s
(!) ด้วย Python 3.4.2awk
+ sort
+cut
3.003s
กับ BSD awk
; 2.388s
กับ GNU awk
(4.1.1); 1.811s
ด้วยmawk
(1.3.4);สำหรับการเปรียบเทียบเพิ่มเติมโซลูชั่นที่ไม่ได้บรรจุเป็นฟังก์ชันข้างต้น:
sort -R
(ไม่ใช่การสลับที่แท้จริงถ้ามีสายอินพุตที่ซ้ำกัน)
10.661s
- การจัดสรรหน่วยความจำเพิ่มเติมดูเหมือนจะไม่สร้างความแตกต่าง24.229s
bash
ลูป + sort
32.593s
บทสรุป :
shuf
ถ้าทำได้ - เร็วที่สุดawk
+ sort
+ cut
คำสั่งผสมเป็นที่พึ่งสุดท้าย ; ซึ่งawk
การดำเนินการที่คุณใช้เรื่อง ( mawk
จะเร็วกว่า GNU awk
, BSD awk
เป็นที่ช้าที่สุด)sort -R
, bash
ลูปและสกาลาของ Windowsรุ่นของงูหลามวิธีการแก้ปัญหา (รหัสงูหลามเป็นเหมือนกันยกเว้นสำหรับรูปแบบในการเสนอราคาและการกำจัดของงบสัญญาณที่เกี่ยวข้องกับการที่ไม่ได้รับการสนับสนุนบนวินโดวส์):
$OutputEncoding
ถ้าคุณต้องการส่งอักขระที่ไม่ใช่ ASCII ผ่านไปป์ไลน์):# Call as `shuf someFile.txt` or `Get-Content someFile.txt | shuf`
function shuf {
$Input | python -c @'
import sys, random, fileinput;
lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write(''.join(lines))
'@ $args
}
โปรดทราบว่า PowerShell สามารถสับเปลี่ยนผ่านGet-Random
cmdlet ได้อย่างเป็นธรรมชาติ (แม้ว่าประสิทธิภาพอาจมีปัญหา) เช่น:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)
cmd.exe
(ไฟล์แบตช์):บันทึกลงไฟล์shuf.cmd
เช่น:
@echo off
python -c "import sys, random, fileinput; lines=[line for line in fileinput.input()]; random.shuffle(lines); sys.stdout.write(''.join(lines))" %*
python -c "import sys, random; lines = [x for x in sys.stdin.read().splitlines()] ; random.shuffle(lines); print(\"\n\".join([line for line in lines]));"
from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL);
ใช้โซลูชันเดิมนั้นเพียงพอและยังคงความยืดหยุ่นในการส่งผ่านอาร์กิวเมนต์ชื่อไฟล์- ไม่จำเป็นต้องเปลี่ยนสิ่งอื่นใด (ยกเว้นการอ้างอิง) - โปรดดูหัวข้อใหม่ที่เพิ่มไว้ใน ด้านล่าง.
ฉันใช้สคริปต์ Perl เล็ก ๆ ซึ่งฉันเรียกว่า "unsort":
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
ฉันยังมีเวอร์ชันที่คั่นด้วย NULL ที่เรียกว่า "unsort0" ... มีประโยชน์สำหรับใช้กับ find -print0 เป็นต้น
PS: โหวตขึ้น 'shuf' ด้วยฉันไม่รู้ว่าอยู่ใน coreutils ในวันนี้ ... ข้างต้นอาจยังมีประโยชน์หากระบบของคุณไม่มี 'shuf'
<STDIN>
ด้วย<>
เพื่อให้โซลูชันทำงานกับอินพุตจากไฟล์ด้วย
นี่คือความพยายามครั้งแรกที่ง่ายใน coder แต่ยากใน CPU ซึ่งเตรียมหมายเลขสุ่มให้กับแต่ละบรรทัดเรียงลำดับแล้วดึงแถบตัวเลขสุ่มจากแต่ละบรรทัด ผลจะถูกเรียงลำดับแบบสุ่ม:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
head myfile | awk ...
ขวาผมแก้ปัญหาด้วย จากนั้นฉันก็เปลี่ยนเป็นแมว นั่นคือเหตุผลที่มันถูกทิ้งไว้ที่นั่น
-k1 -n
เรียงลำดับเนื่องจากผลลัพธ์ของ awk rand()
นั้นเป็นทศนิยมระหว่าง 0 ถึง 1 และเพราะสิ่งที่สำคัญคือมันได้รับการจัดเรียงใหม่อย่างใด -k1
อาจช่วยเร่งความเร็วโดยไม่สนใจส่วนที่เหลือของบรรทัดแม้ว่าเอาต์พุตของ rand () ควรไม่ซ้ำกันมากพอที่จะเปรียบเทียบการลัดวงจร
cat filename |
(หรือ< filename |
) กว่าจำไว้ว่าแต่ละโปรแกรมใช้เวลาใส่ไฟล์ (หรือไม่)
นี่คือสคริปต์ awk
awk 'BEGIN{srand() }
{ lines[++d]=$0 }
END{
while (1){
if (e==d) {break}
RANDOM = int(1 + rand() * d)
if ( RANDOM in lines ){
print lines[RANDOM]
delete lines[RANDOM]
++e
}
}
}' file
เอาท์พุต
$ cat file
1
2
3
4
5
6
7
8
9
10
$ ./shell.sh
7
5
10
9
6
8
2
1
3
4
awk
กับและsort
cut
สำหรับไม่เกินหลายพันบรรทัดมันไม่ได้สร้างความแตกต่างมากนัก แต่เมื่อนับจำนวนบรรทัดที่สูงขึ้นมันก็สำคัญ (เกณฑ์ขึ้นอยู่กับawk
การใช้งาน) ทำให้เข้าใจง่ายขึ้นเล็กน้อยที่จะเปลี่ยนสายwhile (1){
และมีif (e==d) {break}
while (e<d)
หนึ่งซับสำหรับหลาม:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile
และสำหรับการพิมพ์เพียงหนึ่งบรรทัดสุ่ม:
python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile
แต่เห็นโพสต์นี้random.shuffle()
สำหรับข้อเสียของงูหลามของ มันใช้งานไม่ได้กับองค์ประกอบหลายอย่าง (มากกว่า 2080)
/dev/urandom
นั้น random.SystemRandom().shuffle(L)
ที่จะใช้ประโยชน์จากงูหลาม:
.readLines()
ส่งคืนบรรทัดด้วยการขึ้นบรรทัดใหม่
ฟังก์ชั่นที่ใช้ awk แบบง่ายจะทำงาน:
shuffle() {
awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}
การใช้งาน:
any_command | shuffle
สิ่งนี้ควรใช้ได้กับ UNIX เกือบทุกชนิด ทดสอบบน Linux, Solaris และ HP-UX
ปรับปรุง:
โปรดทราบว่าเลขศูนย์นำหน้า ( %06d
) และการrand()
คูณทำให้ทำงานได้อย่างถูกต้องในระบบที่sort
ไม่เข้าใจตัวเลข มันสามารถจัดเรียงตามคำสั่งพจนานุกรม (aka การเปรียบเทียบสตริงปกติ)
"$@"
มันจะทำงานกับไฟล์เป็นอินพุต ไม่มีเหตุผลที่จะทวีคูณrand()
เพราะsort -n
สามารถแยกเศษส่วนทศนิยมได้ มันเป็น แต่เป็นความคิดที่ดีในการควบคุมawk
รูปแบบการออก 's เพราะมีรูปแบบเริ่มต้น%.6g
, rand()
ออกจะจำนวนครั้งคราวในการชี้แจงสัญกรณ์ ในขณะที่การสับเปลี่ยนได้สูงถึง 1 ล้านบรรทัดนั้นเพียงพอในทางปฏิบัติมันเป็นเรื่องง่ายที่จะสนับสนุนไลน์เพิ่มเติมโดยไม่ต้องจ่ายค่าปรับประสิทธิภาพมากนัก %.17f
เช่น
sort
ควรจะสามารถจัดการเศษส่วนทศนิยม (แม้จะมีตัวคั่นหลายพันตามที่ฉันเพิ่งสังเกตเห็น)
ทับทิม FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
puts ARGF.readlines.shuffle
คุณสามารถทำให้มันใช้ได้กับทั้งอินพุต stdin และอาร์กิวเมนต์ชื่อไฟล์
ruby -e 'puts $<.sort_by{rand}'
- ARGF สามารถนับได้แล้วดังนั้นเราจึงสามารถสับเปลี่ยนบรรทัดได้โดยการจัดเรียงตามค่าสุ่ม
หนึ่งซับสำหรับไพ ธ อนตามคำตอบของ scaiแต่ a) ใช้ stdin, b) ทำให้ผลลัพธ์ซ้ำกับเมล็ด c) เลือกเพียง 200 ของบรรทัดทั้งหมด
$ cat file | python -c "import random, sys;
random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
> 200lines.txt
shuf
ที่เรียบง่ายและใช้งานง่ายวิธีที่จะใช้
ตัวอย่าง:
ถือว่าwords.txt
เป็น:
the
an
linux
ubuntu
life
good
breeze
หากต้องการสลับบรรทัดให้ทำ:
$ shuf words.txt
ซึ่งจะโยนเส้นสับเพื่อออกมาตรฐาน ; ดังนั้นคุณต้องไพพ์มันไปที่ไฟล์เอาต์พุตเช่น:
$ shuf words.txt > shuffled_words.txt
การสลับแบบสุ่มหนึ่งครั้งอาจทำให้:
breeze
the
linux
an
ubuntu
good
life
เรามีแพ็คเกจที่จะทำงานได้ดีมาก:
sudo apt-get install randomize-lines
ตัวอย่าง:
สร้างรายการหมายเลขที่เรียงลำดับแล้วบันทึกเป็น 1000.txt:
seq 1000 > 1000.txt
เพื่อสับเปลี่ยนเพียงใช้
rl 1000.txt
นี่เป็นสคริปต์หลามที่ฉันบันทึกเป็น rand.py ในโฟลเดอร์หน้าแรกของฉัน:
#!/bin/python
import sys
import random
if __name__ == '__main__':
with open(sys.argv[1], 'r') as f:
flist = f.readlines()
random.shuffle(flist)
for line in flist:
print line.strip()
บน Mac OSX sort -R
และshuf
ไม่สามารถใช้ได้ดังนั้นคุณสามารถใช้นามแฝงนี้ใน bash_profile ของคุณเป็น:
alias shuf='python rand.py'
ถ้าเช่นฉันคุณมาที่นี่เพื่อมองหาทางเลือกเพื่อshuf
สำหรับ MacOS randomize-lines
แล้วการใช้งาน
ติดตั้งrandomize-lines
(homebrew) แพคเกจซึ่งมีคำสั่งที่มีการทำงานคล้ายกับrl
shuf
brew install randomize-lines
Usage: rl [OPTION]... [FILE]...
Randomize the lines of a file (or stdin).
-c, --count=N select N lines from the file
-r, --reselect lines may be selected multiple times
-o, --output=FILE
send output to file
-d, --delimiter=DELIM
specify line delimiter (one character)
-0, --null set line delimiter to null character
(useful with find -print0)
-n, --line-number
print line number with output lines
-q, --quiet, --silent
do not output any errors or warnings
-h, --help display this help and exit
-V, --version output version information and exit
brew install coreutils
ให้ไบนารีshuf
gshuf
หากคุณติดตั้ง Scala ไว้แล้วนี่เป็นหนึ่งซับเพื่อสับเปลี่ยนอินพุต:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
ฟังก์ชัน bash นี้มีการพึ่งพาน้อยที่สุด (เรียงลำดับและทุบตีเท่านั้น):
shuf() {
while read -r x;do
echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
echo $y
done
}
awk
แต่ประสิทธิภาพจะเป็นปัญหากับอินพุตที่ใหญ่ขึ้น การใช้งานการ$RANDOM
สลับค่าเดียวอย่างถูกต้องมีเพียงบรรทัดอินพุตสูงสุด 32,768 บรรทัดเท่านั้น ในขณะที่คุณสามารถขยายช่วงนั้นได้มันอาจจะไม่คุ้มค่าเช่นบนเครื่องของฉันการรันสคริปต์ของคุณใน 32,768 บรรทัดอินพุตสั้นใช้เวลาประมาณ 1 วินาทีซึ่งประมาณ 150 ครั้งตราบเท่าที่ใช้งาน shuf
และประมาณ 10-15 ครั้ง ตราบใดที่awk
วิธีแก้ปัญหาของตัวเองช่วยOP ใช้ หากคุณสามารถพึ่งพาsort
ได้awk
ควรอยู่ที่นั่นเช่นกัน
ใน windows คุณอาจลองชุดไฟล์นี้เพื่อช่วยให้คุณสลับ data.txt ของคุณการใช้งานของรหัสชุดคือ
C:\> type list.txt | shuffle.bat > maclist_temp.txt
หลังจากออกคำสั่งนี้ maclist_temp.txt จะมีรายการบรรทัดแบบสุ่ม
หวังว่านี่จะช่วยได้
ยังไม่ได้กล่าวถึง:
การใช้unsort
ประโยชน์ ไวยากรณ์ (เป็นเพลย์ลิสต์ที่ค่อนข้างมุ่งเน้น):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]
msort
สามารถสุ่มได้ทีละบรรทัด แต่โดยทั่วไปแล้วจะเป็น overkill:
seq 10 | msort -jq -b -l -n 1 -c r
awk
ตัวแปรอื่น:
#!/usr/bin/awk -f
# usage:
# awk -f randomize_lines.awk lines.txt
# usage after "chmod +x randomize_lines.awk":
# randomize_lines.awk lines.txt
BEGIN {
FS = "\n";
srand();
}
{
lines[ rand()] = $0;
}
END {
for( k in lines ){
print lines[k];
}
}