Sed:更好的方法来解决第n行,其中n是数组的元素

问题描述 投票:0回答:3

我们知道sed命令循环遍历文件的每一行,并且对于每一行,它循环遍历给定的命令列表并执行某些操作。但是当文件非常大时,重复操作的时间和资源成本可能很糟糕。

假设我有一个行号的数组,我想用它作为地址来删除或使用sed命令打印(例如A=(20000 30000 50000 90000)),并且有一个非常大的目标文件。

最简单的方法可能是:(注释@ John1024,注意每个循环的行号更改)

( for NL in ${A[@]}; do sed "$NL d" $very_large_file; done; )>.temp_file;
cp .temp_file $very_large_file; rm .temp_file

上面代码的问题是,对于数组的每个索引行号,它需要遍历整个文件。

为避免这种情况,我们可以:

#COMM=`echo "${A[@]}" | sed 's/\s/d;/g;s/$/d'`;
#sed -i "$COMM" $very_large_file;
#Edited: Better with direct parameter expansion:
sed -i "${A[@]/%/d;}" $very_large_file;

它首先打印数组并用dsed命令替换它的SPACE和END_OF_LINE,这样字符串就像"20000d;30000d;50000d;90000d";在第二行,我们将此字符串视为sed的命令列表。结果是,使用此代码,它只会循环遍历文件一次。

更重要的是,对于就地操作(论证-i),即使感兴趣的最大行数已经过去,也不能使用qsed一起退出,因为如果是这样,那行之后的行(例如90001+)将消失(It似乎就地操作只是用stdout覆盖文件)。

好主意?

(回复@user unknown :)我认为如果我们设法在所有索引行都通过后“退出”循环,它可能会更有效率。出于上述原因,我们不能使用sed -i。将每行打印到文件比复制文件花费更多的时间(例如cat file1 > file2cp file1 file2)。我们可以使用任何其他方法或工具从这个概念中受益。这就是我的期望。

PS:这个问题的要点是“线路位置”和“效率”; “删除行”操作只是一个例子。对于实际任务,还有更多 - 附加/插入/替换,字段分离,案例判断,然后读取/写入文件,计算等。顺序说,它可以调用所有类型的操作,创建子shell或不关心变量传递,...所以,使用的工具应该允许我进行行处理,问题是如何让自己进入感兴趣的行,进行各种操作。

任何评论都表示赞赏。

arrays bash awk sed
3个回答
0
投票

sed用于执行s / old / new,这就是全部,当你为混音添加一个shell循环时,你真的已经离开了轨道(参见https://unix.stackexchange.com/q/169716/133219)。删除数字存储在数组中的行是(使用seq生成输入,因为问题中没有提供样本输入/输出):

$ a=( 3 7 8 )
$ seq 10 |
    awk -v a="${a[*]}" 'BEGIN{split(a,tmp); for (i in tmp) nrs[tmp[i]]} !(NR in nrs)'
1
2
4
5
6
9
10

如果你想在删除最后一个目标行后停止使用awk进行处理并让尾部完成作业,那么你可以预先计算出阵列中的最大值,然后直到最后一个目标行的部分做awk :

max=$( printf '%s\n' "${a[@]}" | sort -rn | head -1 )
head -"$max" file | awk '...' file > out
tail +"$((max+1))" file >> out

idk如果真的比让awk处理整个文件更快,因为awk非常有效,特别是当你没有引用任何字段时所以它不会进行任何字段拆分,但你可以尝试一下。


1
投票

首先复制到文本文件以检查结果。您想要排序第一个最高的行号。

echo "${a[@]}" | sed 's/\s/\n/g' | sort -rn 

你可以使用ed将命令输入printf

printf "%s\n" "command1" "command2" w q testfile | ed -s testfile

结合这些

printf "%s\n" $(echo "${a[@]}" | sed 's/\s/\n/g' | sort -rn | sed 's/$/d/') w q |
   ed -s testfile

编辑(tx @Ed_Morton): 这可以用更少的步骤编写

printf "%s\n" $(printf '%sd\n' "${a[@]}" | sort -rn ) w q | ed -s testfile

我无法删除sort,因为每条删除指令都是从1开始计算亚麻布数。 我试图找到一个编辑文件的命令,而不重定向到另一个,但我开始说你应该复制。我别无选择,我必须赞成直接的awk解决方案,不需要sort


0
投票

您可以从您的行生成中间sed命令文件。

echo ${A[@]} | sort -n > lines_to_delete
min=`head -1` lines_to_delete
max=`head -1` lines_to_delete
# skip to first and from last line, delete the others
sed -i -e 1d -e ${linecount}d -e 's#$#d#' lines_to_delete
head -${min} input > output
sed -f lines_to_delete input >> output
tail -${max} input >> output
mv output input
© www.soinside.com 2019 - 2024. All rights reserved.