【问题标题】:Delete range of lines when line number of known or not in unix using head and tail?使用head和tail在unix中已知或不知道行号时删除行范围?
【发布时间】:2018-07-03 06:54:39
【问题描述】:

这是我的示例文件。

我想这样做。

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

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

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

示例文件文本格式。

输入文件

>     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。

【问题讨论】:

  • 欢迎来到堆栈溢出,请您在代码标签中发布更清晰的示例 Input_file 和示例预期输出文件。
  • @RavinderSingh13 添加了更多输入,感谢您的建议。
  • sed '2,3d' file.txt | tac | sed '2,3d' | tac 也许?
  • @MarkSetchell tac 在 HP unix 版本中不受支持,我只想使用 head 和 tail 来实现。
  • @Minsec,你的系统上甚至没有sedawk

标签: shell unix ksh text-processing


【解决方案1】:

根据 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

【讨论】:

  • 如果以后我想删除最后 30 行,那么我需要手动添加 lines-1 to lines-30,这会使这个脚本变得更长。
  • @Minsec :您的问题非常具体,它不会对这里的许多人有太大的好处。也就是说您应该考虑像 perl 这样的工具
  • @Minsec,请运行我的第一个代码,start_line="2,3 是您需要从 Input_file 的开始处给出您想要跳过的所有行的变量,名为 end_line="2,3" 的变量是您需要从最后一个 Input_file 中给出要跳过的行号。请仔细阅读,如果这对您有帮助,请告诉我。
【解决方案2】:

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

【讨论】:

    【解决方案3】:
      awk '{printf "%d\t%s\n", NR, $0}' < file | sed '2,3d;N;$!P;D' file
    

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

    %d :用于打印数字。你也可以使用'%i'

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

    %s : 打印字符串

    '\n' : 新建一行

    NR : 打印从 1 开始的行号

    对于 sed N:读取/追加下一行输入到模式空间。

    $! : 是为了不删除最后一行

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

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

    【讨论】:

    • 谢谢,但如果我想从顶部和底部删除 2,3(共 2 行),我想删除 6 行,这将不起作用。
    • 感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 它的长期价值通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
    【解决方案4】:

    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
    

    【讨论】:

      【解决方案5】:

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

      #!/bin/ksh
      [ -f "$1" ] || echo "Input is not a file" || exit 1
      total=$(wc -l "$1" | 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" "$1" >tempfile && mv tempfile "$1"
      

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

      备注

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

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

      【讨论】:

        【解决方案6】:

        我很享受这项任务,并为更多可扩展案例(大型文件)编写了awk 脚本。

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

        script.awk

        BEGIN { range = 3}    # define sliding window range
        {lines[NR] = $0}      # 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
        

        【讨论】:

          猜你喜欢
          • 2013-03-24
          • 2011-12-19
          • 1970-01-01
          • 2016-04-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-06
          相关资源
          最近更新 更多