繁体   English   中英

如何在Unix命令行或Shell脚本中对文本文件的行进行混编?

[英]How can I shuffle the lines of a text file on the Unix command line or in a shell script?

我想随机调整文本文件的行并创建一个新文件。 该文件可能有数千行。

我该如何使用catawkcut等方式呢?

您可以使用shuf 至少在某些系统上(在POSIX中似乎没有)。

正如jleedev指出的那样: sort -R也可能是一个选项。 至少在某些系统上; 好吧,你知道了。 有人指出sort -R并没有真正改组,而是根据其哈希值对项目进行排序。

[编者注: sort -R 几乎乱七八糟,除了重复的行/排序键总是彼此相邻结尾。 换句话说:只有使用唯一的输入行/键,它才是真正的随机播放。 确实,输出顺序是由哈希值确定的,但随机性来自选择随机哈希函数 -参见手册

Perl单线将是Maxim解决方案的简单版本

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

该答案通过以下方式补充了许多现有的出色答案:

  • 现有的答案打包在灵活的shell函数中

    • 这些函数不仅接受stdin输入,还接受文件名参数
    • 这些函数采取额外的步骤以通常的方式 (通过退出代码141安静地终止) 处理SIGPIPE ,而不是吵闹。 在将功能输出管道输送到较早关闭的管道(例如管道输送到head时,这一点很重要。
  • 进行性能比较


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' "$@"; }

性能比较:

注意:这些数字是在2012年末配备3.2 GHz Intel Core i5和运行OSX 10.10.3的Fusion Drive的iMac上获得的。 虽然时间会随所使用的OS,机器规格,所用的awk实现而变化 (例如,OSX上使用的BSD awk版本通常比GNU awk慢,尤其是mawk ), 但这应该提供相对性能的一般含义

输入文件是一百万行文件 ,其中seq -f 'line %.0f' 1000000产生。
时间以升序排列(最快的是第一个):

  • shuf
    • 0.090s
  • Ruby 2.0.0
    • 0.289s
  • Perl 5.18.2
    • 0.589s
  • 蟒蛇
    • 使用Python 2.7.6的1.342s 使用Python 3.4.2的2.407s (!)
  • awk + sort + cut
    • BSD awk 3.003s ; 使用GNU awk (4.1.1)的2.388s ; 1.811smawk (1.3.4);

为了进一步比较,上述未打包为功能的解决方案:

  • sort -R (如果输入行重复,则不是真正的随机播放)
    • 10.661s分配更多的内存似乎没有什么不同
  • 斯卡拉
    • 24.229s
  • bash循环+ 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 -Rbash循环和Scala。

Windows版本的Python解决方案 (Python代码相同,除了引号和与信号相关的语句的删除(Windows不支持)之外):

  • 对于PowerShell(在Windows PowerShell中,如果要通过管道发送非ASCII字符,则必须调整$OutputEncoding ):
# 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))" %*

我使用一个很小的perl脚本,我称之为“ unsort”:

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

我也有一个以NULL分隔的版本,称为“ unsort0” ...方便与find -print0等一起使用。

PS:也投票赞成“ shuf”,我不知道这些天在coreutils中有没有……如果您的系统没有“ shuf”,以上内容可能仍然有用。

这是第一次尝试,在编码器上很容易,但在CPU上很难,它会在每行前添加一个随机数,对它们进行排序,然后从每行中剥离该随机数。 实际上,这些行是随机排序的:

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

这是一个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

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个)元素。

简单的基于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不理解数字的系统上也能正常工作。 可以按字典顺序对其进行排序(也称为正常字符串比较)。

Ruby FTW:

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

一种基于scai的答案的 Python衬里,但是a)接受标准输入,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保存在主文件夹中的python脚本:

#!/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 -Rshuf不可用,因此您可以在shuf中将此别名为:

alias shuf='python rand.py'

如果像我一样,您是来这里寻找macOS的shuf的替代品,然后使用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

如果您安装了Scala,则可以使用以下方法来随机调整输入:

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

这个bash函数具有最小的依赖性(仅sort和bash):

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

在Windows中,您可以尝试使用此批处理文件来帮助您重新整理data.txt,批处理代码的用法是

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

发出此命令后,maclist_temp.txt将包含随机的行列表。

希望这可以帮助。

截至目前尚未提及:

  1. unsort util。 语法(某种程度上面向播放列表):

     unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic] [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] [--linefeed] [file ...] 
  2. msort可以按行随机播放,但这通常是过大的:

     seq 10 | msort -jq -b -l -n 1 -cr 

另一个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];
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM