我有一个包含 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"
或者在一行中(定义filename
和number
变量或手动替换它们之后):
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 "$number
number
返回变量在 1 到(包括两个边界)范围内指定的唯一随机数$line_count
。- 例如,
shuf -i 1-6 -n 2
模拟投掷两个常规六面骰子。
- 例如,
printf '%dd;' ARGUMENTS
返回一个格式化的字符串,包含所有(这次没有引用,以便将每个随机数视为单独的参数)。当有剩余参数时,ARGUMENTS
格式字符串将重复,并将替换为表示为十进制数的参数。%dd;
%d
- 因此,例如,的输入
1 7 42
将导致的输出为1d;7d;42d;
。
- 因此,例如,的输入
结果$sed_script
最终就是我们的SCRIPT
for 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
可以传递任意数量的文件(file1
、file2
、...),并且可以通过参数按文件指定要删除的行数,如下lines_to_del
所示。-i inplace
相当于gawk
的sed
-i
另一方面,如果需要从每个文件中删除相同数量的行,您可以lines_to_del
按如下方式设置一次:
gawk -i inplace -v lines_to_del=5 -f del_random file1 file2