unix 命令快速显示文件中间的特定行?

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

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

除了做一些

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

... 这将需要head读取日志文件的前3.47亿行,是否有一个快速和简单的命令,例如将第 347340100 到 347340200 行输出到控制台 ?

更新我完全忘记了 grep 可以打印上下文周围的匹配…这很有效。谢谢!

425062 次浏览

你可以说GNU-grep

grep --context=10 ...

是什么:

tail -n +347340107 filename | head -n 100

我没有测试,但我认为这是可行的。

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

我发现了另外两个解决方案,如果你知道行号,但没有其他(没有grep可能):

假设你需要第20到40行,

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

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

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

我首先将文件分成如下几个较小的文件

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

然后对结果文件进行grep。

不,没有,文件不是行寻址的。

在文本文件中没有固定时间的方法来找到行n的开始。您必须流遍文件并计算换行符。

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

# print line number 52
sed -n '52p' # method 1
sed '52!d' # method 2
sed '52q;d' # method 3,  efficient on large files

方法3对大文件有效 < / >强

显示特定线条的最快方式

sed也需要读取数据来计数行数。 唯一可能的快捷方式是文件中有上下文/顺序可以操作。例如,如果日志行前面有固定宽度的时间/日期等。 你可以使用 unix实用程序在文件中查找特定日期/时间

我更喜欢直接进入less

  • 输入50进入文件的一半,
  • 43210G转到行43210
  • :43210来做同样的事情

诸如此类。

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

基于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
}

要通过<line#>显示<textfile>中的一行,只需执行以下操作:

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

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

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

(提示:如果你的正则表达式中有/,使用m!<regex>!之类的东西代替)

这将打印出<filename>,从匹配<regex1>的行开始,直到(并包括)匹配<regex2>的行。

不需要一个向导,就可以看到一些调整可以使它更加强大。

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

使用

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>

你可以试试这个命令:

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

简单的perl!如果你想从文件中获得第1、3和5行,输入/etc/passwd:

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

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

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

    ex +2p -scq file.txt
    

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

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

    ex +2,5p -scq file.txt
    

    sed -n '2,5p' file.txt

    . sed语法
  • 从给定的行到最后(例如,第5个到文件的末尾):

    ex +5,p -scq file.txt
    

    sed -n '2,$p' file.txt

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

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

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

    . sed语法

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

seq 1 20 > file.txt

解释:

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

我很惊讶,只有另一个答案(由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:]]"; }

得到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.

如果你的行号是100来读取

head -100 filename | tail -1

打印行5

sed -n '5p' file.txt
sed '5q' file.txt

打印第5行以外的所有内容

`sed '5d' file.txt

我用谷歌创建的

#!/bin/bash
#removeline.sh
#remove deleting it comes move line xD


usage() {                                 # Function: Print a help message.
echo "Usage: $0 -l LINENUMBER -i INPUTFILE [ -o OUTPUTFILE ]"
echo "line is removed from INPUTFILE"
echo "line is appended to OUTPUTFILE"
}
exit_abnormal() {                         # Function: Exit with error.
usage
exit 1
}


while getopts l:i:o:b flag
do
case "${flag}" in
l) line=${OPTARG};;
i) input=${OPTARG};;
o) output=${OPTARG};;
esac
done


if [ -f tmp ]; then
echo "Temp file:tmp exist. delete it yourself :)"
exit
fi


if [ -f "$input" ]; then
re_isanum='^[0-9]+$'
if ! [[ $line =~ $re_isanum ]] ; then
echo "Error: LINENUMBER must be a positive, whole number."
exit 1
elif [ $line -eq "0" ]; then
echo "Error: LINENUMBER must be greater than zero."
exit_abnormal
fi
if [ ! -z $output ]; then
sed -n "${line}p" $input >> $output
fi
if [ ! -z $input ]; then
# remove this sed command and this comes move line to other file
sed "${line}d" $input > tmp && cp tmp $input
fi
fi


if [ -f tmp ]; then
rm tmp
fi