我想从一个有 90 行的文本文件中删除 10 行随机行,然后将其输出到一个新文件中。我一直在尝试使用 sed 来做到这一点,但有两个问题。我在用着:
sed -i $((1 + RANDOM & 90))d input.txt > output.txt
然后运行该命令 10 次(我认为有更好的方法来做到这一点!)
我遇到的第一个问题是我收到错误:
sed:-e表达式#1,字符2:行地址0的使用无效
我认为这与它可能已经删除了第 1 行并且正在重试这一事实有关。
第二个问题是,有时没有任何内容写入输出文件,即使它在使用相同命令之前有效。
答案1
您可能想使用RANDOM % 90
而不是&
.这就是零的来源(删除第 1 行就可以了,在下一次运行中,这些行将编号为 1 .. 89)。
但有一个问题:该公式可能会多次生成相同的数字。为了防止这种情况,请使用不同的方法:将数字打乱并选择前十个:
shuf -i1-90 -n10 | sed 's/$/d/' | sed -f- input > output
如果您不喜欢sed
生成sed
脚本,也可以使用printf
:
sed -f <( printf %dd\; $(shuf -i1-90 -n10) ) input > output
答案2
如果您没有shuf
可移植的 GNU ,您可以这样做:
awk -v n=90 -v p=10 '
BEGIN {srand()}
rand() * n-- < p {p--; next}
{print}' < file
它也将比具有高值的 shuf+sed 方法更有效,因为p
它在 o(n) 中,而 shuf+sed 在 o(n*p) 中。当 n=1000000 时,我的系统上的断点在 GNU sed 与 GNU awk 的 p=35 左右,以及 GNU sed 与 mawk 的 p=1 左右(因为在 mawk 中总是更快)。
答案3
我认为这里的挑战是删除 90 行之一,然后删除剩余的 89 行之一,等等——当只剩下 89 行时,我们无法删除第 90 行。
eval $(for i in {90..81}; do CMD="$CMD | sed $(( (RANDOM % $i)+1 ))d"; done; echo cat infile $CMD) > outfile
for 循环累积了一系列字符串,形成管道,其中| sed NNd
NN 是收缩范围中的随机数,从 1 到 90 开始,到 1 到 81 结束,结果是| sed 88d | sed 12d | sed 36d...
命令 CMD 形成后,我们将其添加cat infile
到管道 CMD 之前(请注意,CMD 以|
for 循环中的 a 开头)。 CMD 现在看起来像cat infile | sed 88d | sed 12d...
最后,我们eval
执行命令的CMD字符串并将结果放入outfile
答案4
如果性能没有问题,可以使用这个:
cat PATH_OF_SOURCE_FILE | \
grep -n ^ | \
grep -E "^($(seq 1 90 | shuf | head -n 80 | paste -s -d '|')):" | \
sed 's/[0-9]*:\(.*\)$/\1/' > PATH_TO_TARGET_FILE
第一个grep
索引线;第二个grep
随机选择 80 行,并sed
删除第一个添加的行号grep
。
shuf
注意:如果不需要输出顺序,则通过管道传输最后一个输出