如何从文件中删除随机行?

如何从文件中删除随机行?

我有一个包含 10000 行的文件,我想从中删除 5 个随机确定的行。我该怎么做?

答案1

与使用 for 循环(需要每行处理一次整个文件才能删除)相比,您可能可以更有效地解决问题。

filename="/PATH/TO/FILE"
number=5

line_count="$(wc -l < "$filename")"
line_nums_to_delete="$(shuf -i "1-$line_count" -n "$number")"
sed_script="$(printf '%dd;' $line_nums_to_delete)"

sed -i.bak -e "$sed_script" "$filename"

或者在一行中(定义filenamenumber变量或手动替换它们之后):

sed -i.bak -e "$(printf '%dd;' $(shuf -i "1-$(wc -l < "$filename")" -n "$number"))" "$filename"

-i.bak开关指示sed立即编辑/替换输入文件,但保留原始数据的备份,其名称与输入文件相同,但附加.bak在文件名中。如果您不想制作副本,只需写入-i

顺便说一句,您不必像我一样使用变量。您也可以直接用适当的值替换"$number"和两个出现的"$filename"。我这样做只是为了清楚起见。


分解并解释该命令的其余部分:

sed -e "SCRIPT" "$filename"

sed在变量指定的文件上运行文本处理工具filename,应用作为参数给出的指令SCRIPT

我们的SCRIPT代码在上面几行中动态生成,它们运行命令并将其输出分配给变量。这里我们使用以下命令:

  • wc -l < "$filename"读取变量指定的文件filename并输出该文件包含的行数。

    • 对于您来说,根据您在问题中提到的大小,这应该返回大约 10000。
  • shuf -i "1-$line_count" -n "$numbernumber返回变量在 1 到(包括两个边界)范围内指定的唯一随机数$line_count

    • 例如,shuf -i 1-6 -n 2模拟投掷两个常规六面骰子。
  • printf '%dd;' ARGUMENTS返回一个格式化的字符串,包含所有(这次没有引用,以便将每个随机数视为单独的参数)。当有剩余参数时,ARGUMENTS格式字符串将重复,并将替换为表示为十进制数的参数。%dd;%d

    • 因此,例如,的输入1 7 42将导致的输出为1d;7d;42d;

结果$sed_script最终就是我们的SCRIPTfor sed。一个普通的数字被视为地址,即应用操作的行号,从 1 开始表示输入文件的第一行。d是删除指定行的命令,并;分隔多个sed脚本命令。

总之,整个命令首先检查filename变量中指定的输入文件并计算其行数。然后它会生成number许多范围在 1 到行数之间的唯一随机数,并sed根据这些随机数构建一个脚本以删除每个提到的随机行。最后sed在文件上运行该脚本并对其进行修改。

答案2

您可以使用 for 循环获取随机数并使用 sed 命令删除该行。

for i in {0..5};
 do sed -i "$((1 + RANDOM % 10000))d" filename; 
done

答案3

与 Shivaditya 的答案类似,但没有循环,并且会从整个文件中删除行而不仅仅是前 10 行:

sed -i "$((1+RANDOM%10000))d;$((1+RANDOM%10000))d;$((1+RANDOM%10000))d;$((1+RANDOM%10000))d;$((1+RANDOM%10000))d" filename

将在 1 到 10000 之间选择五个随机数,并通过一次操作删除这些行。

答案4

使用 gawk,将以下代码放入文件(名为 say, del_random

function randint(n)
{
    return int(n * rand()) + 1
}

BEGINFILE {
  command = sprintf("wc -l <\"%s\"", FILENAME)
  command | getline total_lines
  srand()
  delete arr
  while (length(arr) < lines_to_del)
  {
    val = randint(total_lines)
    if (val in arr)
       continue
    arr[val] = 1
  }
}
!(FNR in arr)

然后执行如下命令

gawk -i inplace -f del_random lines_to_del=5 file1 lines_to_del=20 file2

可以传递任意数量的文件(file1file2、...),并且可以通过参数按文件指定要删除的行数,如下lines_to_del所示。-i inplace相当于gawksed-i

另一方面,如果需要从每个文件中删除相同数量的行,您可以lines_to_del按如下方式设置一次:

gawk -i inplace -v lines_to_del=5 -f del_random file1 file2

相关内容