Как перетасовать строки текстового файла в командной строке Unix или в оболочке script?

Я хочу случайным образом перемешать строки текстового файла и создать новый файл. Файл может содержать несколько тысяч строк.

Как я могу сделать это с cat, awk, cut и т.д.?

Ответ 1

Вы можете использовать shuf. На некоторых системах, по крайней мере (не похоже на POSIX).

Как отметил Джеледев: sort -R также может быть вариантом. По крайней мере на некоторых системах; ну, вы получите картину. Было указано, что sort -R не перетасовывает, а сортирует элементы в соответствии с их хэш-значением.

[Примечание редактора: sort -R почти перетасовки, за исключением того, что повторяющиеся строки/ключи сортировки всегда заканчиваются рядом друг с другом. Другими словами: только с уникальными входными строками/клавишами это истинный перетасовка. Хотя верно, что порядок вывода определяется хэш-значениями, случайность возникает из выбора случайной хэш-функции - см. manual.]

Ответ 2

Perl one-liner будет простой версией решения Maxim

perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile

Ответ 3

Этот ответ дополняет многие великие существующие ответы следующими способами:

  • Существующие ответы упакованы в функции гибкой оболочки:

    • Функции принимают не только ввод stdin, но, в качестве альтернативы, также аргументы имени файла
    • Функции предпринимают дополнительные шаги для обработки SIGPIPE обычным способом (тихое завершение с кодом выхода 141), в отличие от шумного взлома. Это важно при передаче вывода функции в канал, который закрыт раньше, например, при передаче в head.
  • Сравнение производительности.


  • Совместимая с POSIX функция на основе awk, sort и cut, адаптированная из собственного ответа OP:
shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "[email protected]" |
               sort -k1,1n | cut -d ' ' -f2-; }
  • Perl -based функция - адаптировано из ответа Moonyoung Kang:
shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "[email protected]"; }
  • -based функция Python, адаптированная из scai answer:
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))
' "[email protected]"; }

See the bottom section for a Windows version of this function.

shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
                     puts ARGF.readlines.shuffle' "[email protected]"; }

Сравнение производительности:

Примечание. Эти цифры были получены на iMac, выпущенном в конце 2012 года, с процессором Intel Core i5 с тактовой частотой 3,2 ГГц и диском Fusion, работающим под управлением OSX 10.10.3. Хотя время зависит от используемой ОС, технических характеристик машины, используемой реализации awk (например, версия BSD awk, используемая в OSX, обычно медленнее, чем GNU awk и особенно mawk), это должно дать общее представление об относительной эффективности.

Входной файл представляет собой файл объемом в 1 миллион строк, созданный с помощью seq -f 'line %.0f' 1000000.
Время указано в порядке возрастания (сначала самое быстрое):

  • shuf
    • 0.090s
  • Ruby 2.0.0
    • 0.289s
  • Perl 5.18.2
    • 0.589s
  • Python
    • 1.342s с Python 2.7.6; 2.407s (!) С Python 3.4.2
  • awk + sort + cut
    • 3.003s с BSD awk; 2.388s с GNU awk (4.1.1); 1.811s с mawk (1.3.4);

Для дальнейшего сравнения решения не упакованы как функции выше:

  • sort -R (не случайный случай, если есть повторяющиеся строки ввода)
    • 10.661s - выделение большего количества памяти, кажется, не имеет значения
  • Scala
    • 24.229s
  • bash loop + sort
    • 32.593s

Выводы:

  • Используйте shuf, если можете - это самый быстрый на сегодняшний день.
  • Ruby преуспевает, за ним следует Perl.
  • Python заметно медленнее, чем Ruby и Perl, и, сравнивая версии Python, 2.7.6 немного быстрее, чем 3.4.1
  • Используйте POSIX-совместимый комбо awk + sort + cut в качестве крайней меры; какая реализация awk вы используете (имеет значение mawk быстрее, чем GNU awk, BSD awk медленнее).
  • Держитесь подальше от петель sort -R, bash и Scala.

Версии решения Python для Windows (код Python идентичен, за исключением изменений в кавычках и удаления операторов, связанных с сигналами, которые не поддерживаются в Windows):

  • Для PowerShell (в Windows PowerShell вам придется настроить $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 (хотя производительность может быть проблемой); например.:
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))" %*

Ответ 4

Я использую крошечный perl script, который я называю "unsort":

#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);

У меня также есть NULL-разделительная версия, называемая "unsort0"... удобная для использования с find -print0 и т.д.

PS: Проголосовал за "shuf" тоже, я понятия не имел, что было в Coreutils в эти дни... вышеупомянутое может по-прежнему быть полезным, если ваши системы не имеют "shuf".

Ответ 5

Вот первая попытка, которая проста в кодере, но сложна на процессоре, который добавляет случайное число к каждой строке, сортирует их, а затем удаляет случайное число из каждой строки. По сути, строки сортируются случайным образом:

cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled

Ответ 6

здесь awk script

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

Ответ 7

Один слой для python:

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

Но см. этот пост за недостатки python random.shuffle(). Это не сработает со многими (более 2080) элементами.

Ответ 8

Простая функция на основе awk выполнит задание:

shuffle() { 
    awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8-
}

использование:

any_command | shuffle

Это должно работать практически в любой UNIX. Протестировано на Linux, Solaris и HP-UX.

Update:

Обратите внимание, что ведущие нули (%06d) и rand() умножают его на правильную работу и на системы, где sort не понимает числа. Его можно сортировать по лексикографическому порядку (сравнение нормальных строк a.k.a.).

Ответ 9

Ruby FTW:

ls | ruby -e 'puts STDIN.readlines.shuffle'

Ответ 10

Один лайнер для Python на основе scai answer, но 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

Ответ 11

Простой и интуитивно понятный способ - использовать 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

Ответ 12

У нас есть пакет для выполнения самой работы:

sudo apt-get install randomize-lines

Пример:

Создайте упорядоченный список чисел и сохраните его до 1000.txt:

seq 1000 > 1000.txt

чтобы перетасовать его, просто используйте

rl 1000.txt

Ответ 13

Это python script, который я сохранил как 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'

Ответ 14

Если вы, как я, вы пришли сюда, чтобы найти альтернативу 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

Ответ 15

Если у вас установлен Scala, здесь один-лайнер для перетасовки ввода:

ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'

Ответ 16

Эта функция bash имеет минимальную зависимость (только сортировка и bash):

shuf() {
while read -r x;do
    echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
    echo $y
done
}

Ответ 17

В окнах Вы можете попробовать этот командный файл, чтобы помочь вам перетасовать файл data.txt. Использование командного кода

C:\> type list.txt | shuffle.bat > maclist_temp.txt

После выполнения этой команды maclist_temp.txt будет содержать рандомизированный список строк.

Надеюсь, что это поможет.

Ответ 18

Пока не указано:

  • unsort util. Синтаксис (несколько ориентированный на плейлист):

    unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
           [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] 
           [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] 
           [--linefeed] [file ...]
    
  • msort может перемещаться по строке, но обычно он переполняется:

    seq 10 | msort -jq -b -l -n 1 -c r
    

Ответ 19

Другой вариант 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];
  }
}