【问题标题】:Quick unix command to display specific lines in the middle of a file?快速 unix 命令在文件中间显示特定行?
【发布时间】:2010-09-16 12:32:44
【问题描述】:

尝试调试服务器问题,我唯一的日志文件是 20GB 的日志文件(甚至没有时间戳!为什么人们使用 System.out.println() 作为日志记录?在生产中?!)

使用 grep,我找到了我想查看的文件区域,第 347340107 行。

除了做类似的事情

head -<$LINENUM + 10> filename | tail -20 

...这需要head 读取日志文件的前 3.47 亿行,是否有一个快速简便的命令可以将行 347340100 - 347340200(例如)转储到控制台?

update 我完全忘记了 grep 可以打印匹配的上下文......这很好用。谢谢!

【问题讨论】:

标签: linux bash unix text


【解决方案1】:

如果您知道行号,我发现了另外两个 solutions,但没有其他信息(无法使用 grep):

假设您需要第 20 到 40 行,

sed -n '20,40p;41q' file_name

awk 'FNR>=20 && FNR<=40' file_name

使用sed 时,在打印最后一行后退出处理比继续处理直到文件末尾更有效。这在开始时大文件和打印行的情况下尤其重要。为此,上面的sed 命令引入了指令41q 以便在第41 行之后停止处理,因为在示例中我们只对第20-40 行感兴趣。您需要将 41 更改为您感兴趣的最后一行,加一。

【讨论】:

    【解决方案2】:
    # print line number 52
    sed -n '52p' # method 1
    sed '52!d' # method 2
    sed '52q;d' # method 3,  efficient on large files 
    

    方法 3 对大文件有效

    显示特定行的最快方式

    【讨论】:

    • 我正试图弄清楚如何调整方法 3 以使用范围而不是单行,但我担心我的 sed-foo 不能胜任这项任务。
    • @XiongChiamiov sed -n '1,500p;501q' 打印 1-500 怎么样?
    • 前两行/方法效率较低的原因是它们继续处理第 52 行之后的所有行,直到最后,而 #3 在打印第 52 行后停止。
    • 这个答案将受益于解释所有论点的作用。
    【解决方案3】:

    使用 GNU-grep 你可以说

    grep --context=10 ...

    【讨论】:

    • 或者更具体地说是 10 行之前:grep -B 10 ... 或者 10 行之后:grep -A 10 ...
    • 这个命令不起作用,下面的 sed -n ',p' 起作用了
    • 这实际上 不是 你想要的,因为即使匹配在最高位,它也会处理整个文件。此时头/尾或尾/头组合更有效。
    • 这根本不满足所提出的问题,因为它没有提供一种按照要求输出特定的方法。
    • 这实际上不是被问到的。 @matt b,你为什么不接受这个答案?
    【解决方案4】:

    不,没有,文件不可行寻址。

    没有固定时间的方法可以在文本文件中找到行 n 的开头。您必须流式传输文件并计算换行符。

    使用完成工作所需的最简单/最快的工具。对我来说,使用headgrep 更有意义,因为后者要复杂得多。我不是说“grep 很慢”,实际上不是,但在这种情况下,如果它比 head 快​​,我会感到惊讶。这基本上是head 中的一个错误。

    【讨论】:

    • 除非行是固定宽度(以字节为单位),否则如果不计算文件开头的换行符,您不知道将文件指针移动到哪里。
    • 这并没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。
    • @exhuma 你是对的。我重写了。七年前,我很生气。 :)
    【解决方案5】:

    怎么样:

    tail -n +347340107 filename | head -n 100
    

    我没有测试它,但我认为它会起作用。

    【讨论】:

    • 不,通常 tail 有 256 最后 KB 或类似的限制,具体取决于版本和操作系统。
    • ? yessire miller
    【解决方案6】:

    我更喜欢直接进入less

    • 键入 50% 转到文件的中间,
    • 43210G转43210行
    • :43210 做同样的事情

    诸如此类。

    更好的是:点击 v 开始编辑(当然是在 vim 中!),在那个位置。现在,请注意 vim 具有相同的键绑定!

    【讨论】:

      【解决方案7】:

      我先把文件分成几个像这样的小文件

      $ split --lines=50000 /path/to/large/file /path/to/output/file/prefix
      

      然后对生成的文件进行 grep。

      【讨论】:

      • 同意,打破该日志并创建一个 cron 作业以正确执行此操作。使用 logrotate 或类似的东西来防止它们变得如此庞大。
      【解决方案8】:

      您可以使用ex 命令,一个标准的 Unix 编辑器(现在是 Vim 的一部分),例如

      • 显示单行(例如第二行):

        ex +2p -scq file.txt
        

        对应的sed语法:sed -n '2p' file.txt

      • 行数范围(例如 2-5 行):

        ex +2,5p -scq file.txt
        

        sed 语法:sed -n '2,5p' file.txt

      • 从给定行到结尾(例如第 5 行到文件末尾):

        ex +5,p -scq file.txt
        

        sed 语法:sed -n '2,$p' file.txt

      • 多行范围(例如 2-4 和 6-8 行):

        ex +2,4p +6,8p -scq file.txt
        

        sed 语法:sed -n '2,4p;6,8p' file.txt

      以上命令可以使用以下测试文件进行测试:

      seq 1 20 > file.txt
      

      解释:

      • +-c 后跟命令 - 读取文件后执行 (vi/vim) 命令,
      • -s - 静音模式,也使用当前终端作为默认输出,
      • q 后跟-c 是退出编辑器的命令(添加! 强制退出,例如-scq!)。

      【讨论】:

        【解决方案9】:

        如果你的行号是100来读

        head -100 filename | tail -1
        

        【讨论】:

          【解决方案10】:

          获取ack

          Ubuntu/Debian 安装:

          $ sudo apt-get install ack-grep
          

          然后运行:

          $ ack --lines=$START-$END filename
          

          例子:

          $ ack --lines=10-20 filename
          

          来自$ man ack

          --lines=NUM
              Only print line NUM of each file. Multiple lines can be given with multiple --lines options or as a comma separated list (--lines=3,5,7). --lines=4-7 also works. 
              The lines are always output in ascending order, no matter the order given on the command line.
          

          【讨论】:

          • 这对我来说似乎是这里所有答案中语法最直观的命令。
          • 从 2019 年 1 月 10 日版本 2.999_06 开始,--lines 参数已被删除。
          【解决方案11】:

          sed 也需要读取数据来计算行数。 可以使用快捷方式的唯一方法是在文件中存在上下文/顺序以进行操作。例如,如果日志行前面带有固定宽度的时间/日期等。 您可以使用 look unix 实用程序对特定日期/时间的文件进行二进制搜索

          【讨论】:

            【解决方案12】:

            使用

            x=`cat -n <file> | grep <match> | awk '{print $1}'`
            

            在这里您将获得匹配发生的行号。

            现在您可以使用以下命令打印 100 行

            awk -v var="$x" 'NR>=var && NR<=var+100{print}' <file>
            

            或者你也可以使用“sed”

            sed -n "${x},${x+100}p" <file>
            

            【讨论】:

            • 如果您有多个匹配项,请使用:"awk 'NR==1{print $1}" 进行第一个匹配项,依此类推
            【解决方案13】:

            使用 sed -e '1,N d; M q',您将打印 N+1 到 M 行。这可能比 grep -C 好一点,因为它不会尝试将行与模式匹配。

            【讨论】:

            • -e 在这里是可选的。
            【解决方案14】:

            以 Sklivvz 的回答为基础,这是一个可以放入 .bash_aliases 文件的不错的函数。从文件前面打印内容时,它对大文件很有效。

            function middle()
            {
                startidx=$1
                len=$2
                endidx=$(($startidx+$len))
                filename=$3
            
                awk "FNR>=${startidx} && FNR<=${endidx} { print NR\" \"\$0 }; FNR>${endidx} { print \"END HERE\"; exit }" $filename
            }
            

            【讨论】:

              【解决方案15】:

              要通过&lt;textfile&gt; 显示来自&lt;textfile&gt; 的行,只需执行以下操作:

              perl -wne 'print if $. == <line#>' <textfile>
              

              如果您想要一种更强大的方式来使用正则表达式显示一系列行——我不会说为什么 grep 这样做是一个坏主意,它应该是相当明显的——这个简单的表达式将向你展示你的在处理约 20GB 的文本文件时,单次传递的范围是您想要的:

              perl -wne 'print if m/<regex1>/ .. m/<regex2>/' <filename>
              

              (提示:如果您的正则表达式中有/,请改用m!&lt;regex&gt;!

              这将从匹配&lt;regex1&gt; 的行开始打印出&lt;filename&gt;,直到(包括)匹配&lt;regex2&gt; 的行。

              无需向导即可了解一些调整如何使其更加强大。

              最后一件事:perl,因为它是一门成熟的语言,有许多隐藏的增强功能以​​提高速度和性能。考虑到这一点,它使其成为此类操作的明显选择,因为它最初是为处理大型日志文件、文本、数据库等而开发的。

              【讨论】:

              • 真的,对我来说似乎不是这样,因为什么时候运行一个 perl 命令比运行 2+ 个通过管道连接的程序更复杂(在页面下方),而且,我认为你实际上是因为我输入了更多需要您阅读的解释,因为页面下方有同样复杂(或更多)并没有被吹出水面的东西......嘘
              • 请注意,用户要求提供一系列行——不过,您的示例可以稍作调整。
              【解决方案16】:

              你可以试试这个命令:

              egrep -n "*" <filename> | egrep "<line number>"
              

              【讨论】:

                【解决方案17】:

                使用 perl 很容易!如果您想从文件中获取第 1、3 和 5 行,例如 /etc/passwd:

                perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
                

                【讨论】:

                • 你说用 awk 很容易,但你用 perl 来做?
                【解决方案18】:

                我很惊讶只有另一个答案(由 Ramana Reddy 提出)建议在输出中添加行号。以下搜索所需的行号并为输出着色。

                file=FILE
                lineno=LINENO
                wb="107"; bf="30;1"; rb="101"; yb="103"
                cat -n ${file} | { GREP_COLORS="se=${wb};${bf}:cx=${wb};${bf}:ms=${rb};${bf}:sl=${yb};${bf}" grep --color -C 10 "^[[:space:]]\\+${lineno}[[:space:]]"; }
                

                【讨论】:

                • 只有代码的答案往往会被标记为删除。您能否就如何解决问题添加一些评论?
                猜你喜欢
                • 2015-04-09
                • 1970-01-01
                • 1970-01-01
                • 2011-10-20
                • 2019-08-04
                • 2015-06-01
                • 2013-01-18
                • 2013-03-24
                • 1970-01-01
                相关资源
                最近更新 更多