如何使用 sed 从文本文件中删除多个随机行?

如何使用 sed 从文本文件中删除多个随机行?

我想从一个有 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 NNdNN 是收缩范围中的随机数,从 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注意:如果不需要输出顺序,则通过管道传输最后一个输出

相关内容