Answers:
这可能不是最有效的方法,但它可以起作用:
shuf <file> > tmp
head -n $m tmp > out1
tail -n +$(( m + 1 )) tmp > out2
具有$m
包含行数。
sort -R
负责随机性。不知道您是否为此答对了票,但请先在手册页中查找。
sort -R
它不会完全随机地对输入进行排序:它会将相同的行分组。因此,如果输入的是例如foo
,foo
,bar
,bar
且m = 2,则一个文件将包含foo
S和其他将同时包含bar
第 GNU coreutils还具有shuf
,它使输入行随机化。另外,您不需要临时文件。
shuf <file> |head -n $m
呢?
此bash / awk脚本随机选择行,并在两个输出文件中保持原始顺序。
awk -v m=4 -v N=$(wc -l <file) -v out1=/tmp/out1 -v out2=/tmp/out2 \
'BEGIN{ srand()
do{ lnb = 1 + int(rand()*N)
if ( !(lnb in R) ) {
R[lnb] = 1
ct++ }
} while (ct<m)
} { if (R[NR]==1) print > out1
else print > out2
}' file
cat /tmp/out1
echo ========
cat /tmp/out2
根据问题中的数据输出。
12345
23456
200
600
========
67891
-20000
20
与Unix一样,该TM也有一个实用程序。
每日程序:split
split
将以许多不同的方式分割文件,-b
字节,-l
行,-n
输出文件数。我们将使用该-l
选项。由于您要选择随机行,而不仅仅是第一行m
,因此我们将sort
首先随机选择文件。如果您想阅读sort
,请在这里参考我的答案。
现在,实际的代码。这很简单,真的:
sort -R input_file | split -l $m output_prefix
这将创建两个文件,一个文件包含m
行,一个文件包含N-m
行,分别名为output_prefixaa
和output_prefixab
。确保m
是您想要的较大文件,否则您将获得多个长度为m
1的文件(其中一个带有N % m
)。
如果要确保使用正确的大小,请执行以下代码:
m=10 # size you want one file to be
N=$(wc -l input_file)
m=$(( m > N/2 ? m : N - m ))
sort -R input_file | split -l $m output_prefix
编辑:引起我注意的是某些sort
实现没有-R
标记。如果有perl
,可以替代perl -e 'use List::Util qw/shuffle/; print shuffle <>;'
。
sort -R
似乎只存在于某些版本中(可能是gnu版本)。对于其他平台,我编写了一个名为“ randline”的工具,该工具只对stdin进行随机化处理。任何需要的人都可以在beesbuzz.biz/code上找到。(我倾向于将文件内容改组很多。)
sort -R
它不会完全随机地对输入进行排序:它会将相同的行分组。因此,如果输入的是例如foo
,foo
,bar
,bar
且m = 2,则一个文件将包含foo
S和其他将同时包含bar
第 GNU coreutils还具有shuf
,它使输入行随机化。另外,您可以使用head
和tail
代替选择输出文件名split
。
如果您不介意对行进行重新排序,并且拥有GNU coreutils(即,在非嵌入式Linux或Cygwin上,自shuf
6.0版以来就不太古老),shuf
(“随机播放”)将对文件的行进行随机重新排序。因此,您可以将文件随机播放,并将前m行分配到一个文件中,而将其余m行分配到另一个文件中。
没有理想的方式来执行该调度。您不能只是连锁head
,tail
因为head
会向前缓冲。您可以使用split
,但是在输出文件名方面没有任何灵活性。您可以使用awk
,当然:
<input shuf | awk -v m=$m '{ if (NR <= m) {print >"output1"} else {print} }'
您可以使用sed
,它比较晦涩,但对于大文件来说可能更快。
<input shuf | sed -e "1,${m} w output1" -e "1,${m} d" >output2
或者tee
,如果您的平台具有,则可以使用来复制数据/dev/fd
。如果m很小就可以:
<input shuf | { tee /dev/fd/3 | head -n $m >output1; } 3>&1 | tail -n +$(($m+1)) >output2
可移植的是,您可以使用awk依次分派每行。请注意,awk在初始化其随机数生成器方面不是很好。随机性不仅绝对不适合密码学,而且对于数值模拟也不是很好。在一秒钟的时间内,任何系统上所有awk调用的种子都相同。
<input awk -v N=$(wc -l <input) -v m=3 '
BEGIN {srand()}
{
if (rand() * N < m) {--m; print >"output1"} else {print >"output2"}
--N;
}'
如果需要更好的随机性,则可以在Perl中做同样的事情,这会很好地植入RNG。
<input perl -e '
open OUT1, ">", "output1" or die $!;
open OUT2, ">", "output2" or die $!;
my $N = `wc -l <input`;
my $m = $ARGV[0];
while (<STDIN>) {
if (rand($N) < $m) { --$m; print OUT1 $_; } else { print OUT2 $_; }
--$N;
}
close OUT1 or die $!;
close OUT2 or die $!;
' 42
awk
例如: -v N=$(wc -l <file) -v m=4
...这只是打印“随机”线的时候,随机值是小于$m
,而不是打印$m
随机线......如此看来,perl
可能是用做同样的事情兰特,但我不perl
不太了解编译错误:-e第7行的语法错误,靠近“)print”
shuf
示例中一个特别有趣且有用的文件拆分方法。
head
cat
组合导致在随后的第二检测数据的丢失3-4 .... TEST 1-2 { for i in {00001..10000} ;do echo $i; done; } | { head -n 5000 >out1; cat >out2; }
.. TEST 3-4 { for i in {00001..10000} ;do echo $i; done; } >input; cat input | { head -n 5000 >out3; cat >out4; }
... wc -l
为的结果输出TEST 1-2是5000 5000(良好),但对于试验3-4是5000 4539(不好)。。差异取决于所涉及的文件大小...这是我的测试代码
head
阅读;它前面读取的内容和未打印的内容将被丢弃。我已经用不太优雅但(我可以肯定)正确的解决方案更新了答案。
假设m = 7
和N = 21
:
cp ints ints.bak
for i in {1..7}
do
rnd=$((RANDOM%(21-i)+1))
# echo $rnd;
sed -n "${rnd}{p,q}" 10k.dat >> mlines
sed -i "${rnd}d" ints
done
注意:如果您用7
诸如$1
或的变量替换$m
,则必须使用seq
,而不是{from..to}
-notation,它不会进行变量扩展。
它的工作原理是从文件中逐行删除,这会越来越短,因此可以删除的行号必须越来越小。
including them
原始行,因此including
,不是consisting of
,并且不使用only
,但是我想您的解释是user288609的含义。我将相应地调整脚本。
+1
在错误的位置。它应该是rnd=$((RANDOM%(N-i)+1))
其中N =在你的榜样21 ..这导致目前sed
崩溃时rnd
被评估为0
。..而且,随着所有文件重写,它的伸缩性也不太好。例如123秒来提取10,000行的文件5000条随机线与 0.03秒有更直接的方法...