在 unix 中使用 head 和 tail 删除行号已知或未知时的行范围?

Delete range of lines when line number of known or not in unix using head and tail?

这是我的示例文件。

我想做这个。

  1. 我有固定要求删除第 2 行和第 3 行,保留第 1 行。
  2. 从底部开始,我想删除除最后一行以外的 2 行,因为我不知道最后一行的编号是多少,因为它取决于文件。

一旦我删除了第 2 行和第 3 行,第 4 行理想情况下应该出现在第 2 行,依此类推,删除后的底部也是如此。

我想使用 head/tail 命令并仅修改现有文件。作为更改写回同一个文件。

示例文件文本格式。

输入文件

>     This is First Line
>     Delete Delete Delete This Line
>     Delete Delete Delete This Line
>     ..
>     ..
>     ..
>     ..
>     Delete Delete Delete This Line
>     Delete Delete Delete This Line
>     This is Last Line, should not be deleted It could be come at any line 

数量(变量)

输出文件(同一个文件修改)

This is First Line
..
..
..
..
This is Last Line, should not be deleted It could be come at any line number (variable)

编辑 - 由于 Unix 上的兼容性问题(在 ksh shell 上使用 HP Unix)我想使用 head/tail/awk 来实现它。没有 sed.

根据OP的要求添加解决方案,使其成为正版解决方案。

方法: 在这个解决方案中,OP 可以提供从任何 Input_file 和那些的起点和终点的线将跳过行。

代码会做什么: 我已经用这种方式编写了代码,它会根据你的代码生成 awk 代码给定要跳过的行,然后也会 运行 它。

cat print_lines.ksh
start_line="2,3"
end_line="2,3"
total_lines=$(wc -l<Input_file)

awk -v len="$total_lines" -v OFS="||" -v s1="'" -v start="$start_line" -v end="$end_line" -v lines=$(wc -l <Input_file) '
BEGIN{
  num_start=split(start, a,",");
  num_end=split(end, b,",");
  for(i=1;i<=num_start;i++){
    val=val?val OFS "FNR=="a[i]:"FNR=="a[i]};
  for(j=1;j<=num_end;j++){
    b[j]=b[j]>1?len-(b[j]-1):b[j];
    val=val?val OFS "FNR=="b[j]:"FNR=="b[j]};
print "awk " s1 val "{next} 1" s1" Input_file"}
' | sh

Input_file 名称更改为您的实际文件名,然后让我知道进展如何。


以下awk可能对你有帮助(因为我没有Hp系统所以没有测试)。

awk -v lines=$(wc -l <Input_file) 'FNR==2 || FNR==3 || FNR==(lines-1) || FNR==(lines-2){next} 1'  Input_file

编辑:现在也添加非单线形式的解决方案。

awk -v lines=$(wc -l <Input_file) '
FNR==2 || FNR==3 || FNR==(lines-1) || FNR==(lines-2){
next}
1
'  Input_file

wc + sed 解法:

len=$(wc -l inpfile | cut -d' ' -f1)
sed "$(echo "$((len-2)),$((len-1))")d; 2,3d" inpfile > tmp_f && mv tmp_f inpfile

$ cat inputfile
>     This is First Line
>     ..
>     ..
>     ..
>     ..
>     This is Last Line, should not be deleted It could be come at any line
  awk '{printf "%d\t%s\n", NR, [=10=]}' < file | sed '2,3d;N;$!P;D' file

此处的 awk 用于提供行号,然后将输出传递给 sed,sed 使用行号执行所需的操作。

%d : 用于打印数字。您也可以使用“%i”

'\t' : 用于在数字和字符串之间放置制表符

%s : 打印字符串

'\n' : 新建一行

NR :打印从 1

开始的行号

对于 sed N:Read/append下一行输入模式space.

$! : 不删除最后一行

D :当模式 space 不包含正常的新行并开始新的循环时使用,就好像发出了 d 命令一样。否则,删除模式 space 中指定行的文本,并使用结果模式 space 重新开始循环,而不读取新的输入行。

P : 打印到当前模式的第一个嵌入换行符 space.This 删除主题行后打印行。

Perl 建议...将整个文件读入数组 @L,获取最后一行的索引。删除倒数第二行、倒数第三行、第三行和第二行。打印剩下的。

perl -e '@L=<>; $p=$#L; delete $L[$p-1]; delete $L[$p-2]; delete $L[2]; delete $L[1]; print @L' file.txt

或者,用 splice 更简洁一点:

perl -e '@L=<>; splice @L,1,2; splice @L,$#L-2,2; print @L' file.txt

如果您希望有一些灵活性,ksh script 方法可能会奏效,但在资源方面并不昂贵:

#!/bin/ksh
[ -f "" ] || echo "Input is not a file" || exit 1
total=$(wc -l "" | cut -d' ' -f1 )
echo "How many lines to delete at the end?"
read no
[ -z "$no" ] && echo "Not sure how many lines to delete, aborting" && exit 1
sed "2,3d;$((total-no)),$((total-1))d" "" >tempfile && mv tempfile ""

并将文件作为参数提供给脚本。

备注

  • 这将删除第二行和第三行。
  • 加上 no 从用户读取的最后一行不包括最后一行的行数。

注:我的ksh版本是93u+ 2012-08-01

我喜欢这个任务,并为更具可扩展性的案例(大文件)编写了 awk 脚本。

Reading/scanning 输入文件一次(无需知道行数),而不是将整个文件存储在内存中。

script.awk

BEGIN { range = 3}    # define sliding window range
{lines[NR] = [=10=]}      # capture each line in array
NR == 1 {print}       # print 1st line
NR > range * 2{       # for lines in sliding window range bottom
    print lines[NR - range]; # print sliding window top line
    delete lines[NR - range];   # delete sliding window top line
}
END {print}           # print last line

运行:

awk -f script.awk input.txt

input.txt

line 1
line 2
line 3
line 4
line 5
line 6
line 7
line 8
line 9
line 10

输出:

line 1
line 4
line 5
line 6
line 7
line 10