如何截取需要的日志?--sed

小知识,大用处

今天在帮同事拉日志的时候,突然发现需要的日志太大了,然后就想着sed,然后按照要求拉取2011.07.25的日志,命令如下:

sed -n '/2011.07.25/,$p' FO_GROUP~APP_OC4J~FO_GROUP~1.log > FO_GROUP~APP_OC4J~FO_GROUP~1-as31.log

sed -i '/memcache/i\\= ' test.log 【i\表示插入文本,但显示的是插入匹配行的前面,之所以有两个\,是因为其中一个是转义字符。

sed -i '/memcache/i\\= ' test.log 【a\表示在文本,但显示的是插入匹配行的后面】

sed '/myip/r a.txt' test.log

下面我们对sed进行一个汇总

总结:

1.Sed简介

sed是一种在线编辑器它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。以下介绍的是Gnu版本的Sed 3.02。

2.定址

可以通过定址来定位你所希望编辑的行,该地址用数字构成,用逗号分隔的两个行数表示以这两行为起止的行的范围(包括行数表示的那两行)。如1,3表示1,2,3行,美元符号($)表示最后一行。范围可以通过数据,正则表达式或者二者结合的方式确定 。

3.Sed命令

调用sed命令有两种形式:

  • sed [options] 'command' file(s)

  • sed [options] -f scriptfile file(s)

a\

在当前行后面加入一行文本

b lable

分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。

c\

用新的文本改变本行的文本。

d

从模板块(Pattern space)位置删除行。

D

删除模板块的第一行。

i\

在当前行上面插入文本。

h

拷贝模板块的内容到内存中的缓冲区。

H

追加模板块的内容到内存中的缓冲区

g

获得内存缓冲区的内容,并替代当前模板块中的文本。

G

获得内存缓冲区的内容,并追加到当前模板块文本的后面。

l

列表不能打印字符的清单。

n

读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。

N

追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。

p

打印模板块的行。

P(大写)

打印模板块的第一行。

q

退出Sed。

r file

从file中读行。

t label

if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。

T label

错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。

w file

写并追加模板块到file末尾。

W file

写并追加模板块的第一行到file末尾。

!

表示后面的命令对所有没有被选定的行发生作用。

s/re/string

用string替换正则表达式re。

=

打印当前行号码。

#

把注释扩展到下一个换行符以前。

以下的是替换标记
  • g表示行内全面替换。

  • p表示打印行。

  • w表示把行写入一个文件。

  • x表示互换模板块中的文本和缓冲区中的文本。

  • y表示把一个字符翻译为另外的字符(但是不用于正则表达式)

4.选项

-e command, --expression=command

允许多台编辑。

-h, --help

打印帮助,并显示bug列表的地址。

-n, --quiet, --silent

取消默认输出。

-f, --filer=script-file

引导sed脚本文件名。

-V, --version

打印版本和版权信息。

5.元字符集

^

锚定行的开始 如:/^sed/匹配所有以sed开头的行。

$

锚定行的结束 如:/sed$/匹配所有以sed结尾的行。

.

匹配一个非换行符的字符 如:/s.d/匹配s后接一个任意字符,然后是d。

*

匹配零或多个字符 如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。

[]

匹配一个指定范围内的字符,如/[Ss]ed/匹配sed和Sed。

[^]

匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。

\(..\)

保存匹配的字符,如s/\(love\)able/\1rs,loveable被替换成lovers。

&

保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**。

\<

锚定单词的开始,如:/\<love/匹配包含以love开头的单词的行。

\>

锚定单词的结束,如/love\>/匹配包含以love结尾的单词的行。

x\{m\}

重复字符x,m次,如:/0\{5\}/匹配包含5个o的行。

x\{m,\}

重复字符x,至少m次,如:/o\{5,\}/匹配至少有5个o的行。

x\{m,n\}

重复字符x,至少m次,不多于n次,如:/o\{5,10\}/匹配5--10个o的行。

6.实例

删除:d命令
  • $ sed '2d' example-----删除example文件的第二行。

  • $ sed '2,$d' example-----删除example文件的第二行到末尾所有行。

  • $ sed '$d' example-----删除example文件的最后一行

  • $ sed '/test/'d example-----删除example文件所有包含test的行

替换:s命令
  • $ sed 's/test/mytest/g' example-----在整行范围内把test替换为mytest。如果没有g标记,则只有每行第一个匹配的test被替换成mytest

  • $ sed -n 's/^test/mytest/p' example-----(-n)选项和p标志一起使用表示只打印那些发生替换的行。也就是说,如果某一行开头的test被替换成mytest,就打印它。

  • $ sed 's/^192.168.0.1/&localhost/' example-----&符号表示替换换字符串中被找到的部份。所有以192.168.0.1开头的行都会被替换成它自已加localhost,变成192.168.0.1localhost

  • $ sed -n 's/\(love\)able/\1rs/p' example-----love被标记为1,所有loveable会被替换成lovers,而且替换的行会被打印出来。

  • $ sed 's#10#100#g' example-----不论什么字符,紧跟着s命令的都被认为是新的分隔符,所以,“#”在这里是分隔符,代替了默认的“/”分隔符。表示把所有10替换成100。

选定行的范围:逗号
  • $ sed -n '/test/,/check/p' example-----所有在模板test和check所确定的范围内的行都被打印

  • $ sed -n '5,/^test/p' example-----打印从第五行开始第一个包含以test开始的行之间的所有行。

  • $ sed '/test/,/check/s/$/sed test/' example-----对于模板test和west之间的行,每行的末尾用字符串sed test替换

多点编辑:e命令
  • $ sed -e '1,5d' -e 's/test/check/' example-----(-e)选项允许在同一行里执行多条命。如例子所示,第一条命令删除1至5行,第二条命令用check替换test。命令的执行顺序对结果有影响。如果两个命令都是替换命令,那么第一个替换命令将影响第二个替换命令的结果。

  • $ sed --expression='s/test/check/' --expression='/love/d' example-----一个比-e更好的命令是--expression。它能给sed表达式赋值。

从文件读入:r命令
  • $ sed '/test/r file' example-----file里的内容被读进来,显示在与test匹配的行后面,如果匹配多行,则file的内容将显示在所有匹配行的下面。

写入文件:w命令
  • $ sed -n '/test/w file' example-----在example中所有包含test的行都被写入file里。

追加命令:a命令
  • $ sed '/^test/a\\--->this is a example' example<-----'this is a example'被追加到以test开头的行后面,sed要求命令a后面有一个反斜杠

插入:i命令

$ sed '/test/i\\

new line

-------------------------' example

如果test被匹配,则把反斜杠后面的文本插入到匹配行的前面

下一个:n命令
  • $ sed '/test/{ n; s/aa/bb/; }' example-----如果test被匹配,则移动到匹配行的下一行,替换这一行的aa,变为bb,并打印该行,然后继续。

变形:y命令
  • $ sed '1,10y/abcde/ABCDE/' example-----把1--10行内所有abcde转变为大写,注意,正则表达式元字符不能使用这个命令

退出:q命令
  • $ sed '10q' example-----打印完第10行后,退出sed。

保持和获取:h命令和G命令
  • $ sed -e '/test/h' -e '$G example-----在sed处理文件的时候,每一行都被保存在一个叫模式空间的临时缓冲区中,除非行被删除或者输出被取消,否则所有被处理的行都将打印在屏幕上。接着模式空间被清空,并存入新的一行等待处理。在这个例子里,匹配test的行被找到后,将存入模式空间,h命令将其复制并存入一个称为保持缓存区的特殊缓冲区内。第二条语句的意思是,当到达最后一行后,G命令取出保持缓冲区的行,然后把它放回模式空间中,且追加到现在已经存在于模式空间中的行的末尾。在这个例子中就是追加到最后一行。简单来说,任何包含test的行都被复制并追加到该文件的末尾。

保持和互换:h命令和x命令
  • $ sed -e '/test/h' -e '/check/x' example -----互换模式空间和保持缓冲区的内容。也就是把包含test与check的行互换。

7.脚本

Sed脚本是一个sed的命令清单,启动Sed时以-f选项引导脚本文件名。Sed对于脚本中输入的命令非常挑剔,在命令的末尾不能有任何空白或文本,如果在一行中有多个命令,要用分号分隔。以#开头的行为注释行,且不能跨行。

8.小技巧

  • 在sed的命令行中引用shell变量时要使用双引号,而不是通常所用的单引号。下面是一个根据name变量的内容来删除named.conf文件中zone段的脚本:

    name='zone\ "localhost"' sed "/$name/,/};/d" named.conf

<think> 我们有一份日志文件,日志每行的格式通常是:时间戳 + 日志内容。我们想要截取某个时间段内的日志。 假设时间戳的格式是标准的,比如:2023-01-01 12:00:00 我们可以使用grep、sed、awk中的任意一个或多个来实现,但awk在处理时间范围时更为灵活。 方法1:使用grep(适用于时间格式固定且日志行以时间戳开头的情况) grep可以通过正则表达式匹配时间范围,但要注意,如果时间跨天,简单的正则可能不够。 例如:截取2023-01-01 12:00到13:00的日志 grep -E '2023-01-01 12:[0-5][0-9]:[0-5][0-9]' logfile # 只能匹配12:00-12:59 对于12:00到13:00,我们可以用两个正则表达式: grep -E '2023-01-01 (12:[0-5][0-9]:[0-5][0-9]|13:00:00)' logfile 但这样写很麻烦,而且如果时间段较长(比如跨小时、跨天)就不实用了。 方法2:使用awk(更通用) awk可以比较方便地处理时间范围,但需要将时间字符串转换为时间戳(数字)进行比较,或者按字典序比较(如果时间格式是固定且规范的,字典序比较即可)。 假设日志格式为:2023-01-01 12:00:00 [其他内容] 我们可以用如下方式: awk ' BEGIN { # 定义开始时间和结束时间,格式必须与日志中的时间格式一致 start = "2023-01-01 12:00:00" end = "2023-01-01 13:00:00" } { # 提取每行的时间字段,假设时间在日志行的开头,且长度为19 # 注意:日志中可能包含其他内容,我们取前19个字符作为时间 timestamp = substr($0,1,19) # 比较:如果当前行的时间戳在[start, end]区间内,则打印 if (timestamp >= start && timestamp <= end) { print } }' logfile 但是,这种方法要求日志中的时间格式必须与start和end的格式严格一致,并且是按照字典序可比较的(即年月日时分秒的排列使得字符串比较等同于时间先后比较)。 如果时间格式不是固定长度呢?或者时间戳不在行首?那么我们需要使用字段分隔。 假设日志中时间戳是第一个字段,并且字段分隔符是空格(但日期和时间之间也有空格,所以日期和时间会被分成两个字段),那么我们可以这样: 日志示例:2023-01-01 12:00:00 INFO ... 我们可以将前两个字段合并为时间字符串: awk -v start="2023-01-01 12:00:00" -v end="2023-01-01 13:00:00" ' { # 将第一列和第二列合并,中间加空格 timestamp = $1" "$2 if (timestamp >= start && timestamp <= end) { print $0 } }' logfile 注意:这种方法要求时间戳在日志行中作为前两个字段出现,并且格式与start和end一致。 如果时间格式不是标准格式(比如包含毫秒),或者我们需要处理跨天的情况,那么可能需要将时间转换为时间戳(Unix时间戳,即从1970年1月1日开始的秒数)再比较。 例如,时间格式为:2023-01-01 12:00:00.123 我们可以使用awk的mktime函数,但mktime函数只支持到秒,不支持毫秒。因此,我们可以先去掉毫秒部分,或者将毫秒转换为小数。 转换为时间戳的例子(假设时间格式为:2023-01-01 12:00:00): awk -v start="2023-01-01 12:00:00" -v end="2023-01-01 13:00:00" ' BEGIN { # 将开始和结束时间转换为时间戳 # 注意:mktime的输入格式为 "YYYY MM DD HH MM SS",所以我们需要转换一下 split(start, a, /[-: ]/); start_epoch = mktime(a[1]" "a[2]" "a[3]" "a[4]" "a[5]" "a[6]); split(end, b, /[-: ]/); end_epoch = mktime(b[1]" "b[2]" "b[3]" "b[4]" "b[5]" "b[6]); } { # 提取当前行的时间字符串(假设前两个字段) timestamp_str = $1" "$2; split(timestamp_str, c, /[-: ]/); if (c[1]!="" && c[2]!="" && c[3]!="" && c[4]!="" && c[5]!="" && c[6]!="") { current_epoch = mktime(c[1]" "c[2]" "c[3]" "c[4]" "c[5]" "c[6]); if (current_epoch >= start_epoch && current_epoch <= end_epoch) { print $0; } } }' logfile 注意:这种方法要求时间戳是日志行的前两个字段,且格式为“年--日 时:分:秒”(无毫秒)。如果有毫秒,我们可以先去掉毫秒部分再处理。 方法3:使用sed(不太推荐,因为sed是按行处理,对时间范围的判断不如awk方便) 通常,如果时间范围是连续的,且日志是按照时间顺序排列的,我们可以使用sed的地址范围,但要求时间格式在日志中连续出现,并且可以用正则表达式表示。 例如:截取从2023-01-01 12:00:00开始到2023-01-01 13:00:00结束的日志 但是,正则表达式无法直接表示一个时间区间(除非是连续的,比如同一小时内),所以不常用。 如果日志已经按时间排序,我们可以用两个正则表达式来定位开始行和结束行,然后打印之间的内容: sed -n '/2023-01-01 12:00:00/,/2023-01-01 13:00:00/p' logfile 但是注意:这种方式会匹配到第一个开始时间模式出现的位置,到第一个结束时间模式出现的位置。如果日志中多次出现这些时间,可能不准确。 另外,如果结束时间模式没有匹配到,则会一直打印到文件末尾。而且,如果开始时间模式匹配了多行,可能会提前开始。 所以,使用sed的地址范围需要谨慎,它适用于时间戳严格递增且连续,并且时间段内的时间戳不会重复出现的情况。 根据不同的日志格式和需求,选择合适的方法。一般来说,使用awk更为灵活和准确。 下面给出一个综合的例子,使用awk,适用于时间戳在行首且格式为“YYYY-MM-DD HH:MM:SS”的情况: awk -v start="2023-01-01 12:00:00" -v end="2023-01-01 13:00:00" ' $1" "$2 >= start && $1" "$2 <= end' logfile 如果时间戳不是前两个字段,而是整个日志的第一个部分(比如合并在一起),那么需要调整字段的提取。 如果日志格式复杂,可能需要更复杂的awk脚本来提取时间戳。 注意:以上方法均假设日志已经按照时间顺序排列。如果日志是乱序的,那么需要先排序,但通常日志是按时间顺序写入的。 另外,如果日志时间戳格式包含时区,或者有其他格式,需要相应调整。 根据引用[2]中提到的,awk适合格式化文本,因此推荐使用awk来处理时间段日志截取。 总结: 1. 使用grep适用于简单的时间匹配(比如同一小时内的分钟和秒在特定范围),但不适合跨小时或跨天。 2. 使用awk可以灵活处理,通过字符串比较或转换为时间戳比较,推荐。 3. 使用sed适用于连续的时间段,且日志按时间顺序排列,但需要确保时间模式不会重复匹配。 因此,我们推荐使用awk方法。 示例命令(假设时间戳是前两个字段): awk -v start="2023-01-01 12:00:00" -v end="2023-01-01 13:00:00" '$1" "$2>=start && $1" "$2<=end' logfile 如果时间段是今天12:00到13:00,可以动态设置日期(例如使用当前日期),但需要结合shell命令生成日期字符串。 例如,获取今天日期: today=$(date +"%Y-%m-%d") start="${today} 12:00:00" end="${today} 13:00:00" 然后传入awk: awk -v start="$start" -v end="$end" '$1" "$2>=start && $1" "$2<=end' logfile 如果日志文件很大,我们还可以使用awk的退出条件:当时间超过结束时间时退出,以节省时间(前提是日志按时间排序)。 awk -v start="2023-01-01 12:00:00" -v end="2023-01-01 13:00:00" ' $1" "$2 > end { exit } # 如果当前行时间已经超过结束时间,则退出 $1" "$2 >= start && $1" "$2 <= end { print }' logfile 这样,当遇到第一个超过结束时间的日志行时,awk就会退出,从而避免读取整个文件。 请根据实际日志格式调整命令。 注意:如果日志时间戳格式不一致,或者有缺失,需要做额外处理。 </think>要使用 Linux 三剑客(grep、sed、awk)截取特定时间段的日志,具体方法取决于日志格式和时间精度需求。以下是三种常用方法: ### 📅 方法 1:使用 awk(推荐 - 最灵活) ```bash awk -v start="2023-10-01 08:00:00" -v end="2023-10-01 09:30:00" ' { # 提取前两个字段组成完整时间戳 (假设第1列为日期,第2列为时间) timestamp = $1 " " $2 if (timestamp >= start && timestamp <= end) print $0 }' access.log ``` **说明**: 1. 通过 `-v` 传递起止时间参数 2. 比较时间戳字符串(要求日志时间格式为 `YYYY-MM-DD HH:MM:SS`) 3. 若日志包含毫秒,需调整字段提取:`timestamp = $1 " " $2 " " $3` 4. **优势**:天然支持时间范围判断,处理跨小时/跨天场景 ### 🔍 方法 2:使用 grep(简单时间段) ```bash # 提取 08:00:00 至 08:59:59 的日志 grep -E '2023-10-01 08:[0-5][0-9]:[0-5][0-9]' access.log # 精确到分钟 (08:30-08:45) grep -E '2023-10-01 08:(30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45)' access.log ``` **说明**: - 通过正则匹配时间模式 - **局限**:无法直接处理跨小时(如 08:55-09:05) - **技巧**:跨小时需合并查询 ```bash grep -E '2023-10-01 08:5[5-9]|2023-10-01 09:0[0-5]' access.log ``` ### ✂️ 方法 3:使用 sed(连续时间段) ```bash # 提取从"2023-10-01 08:00"到"2023-10-01 09:00"的日志sed -n '/2023-10-01 08:00:00/,/2023-10-01 09:00:00/p' access.log ``` **说明**: - `地址范围模式`匹配两个模式之间的行 - **要求**:日志必须严格按时间排序 - **风险**:若结束时间不存在,会输出到文件末尾 --- ### ⚙️ 高级场景处理 #### 处理带毫秒的时间戳 ```bash awk -v start="2023-10-01 08:00:00" -v end="2023-10-01 09:30:00" ' { # 提取前3个字段 (日期+时间+毫秒) ts = $1 " " $2 " " $3 gsub(/[.,]/, "", ts) # 移除毫秒分隔符 ts_epoch = mktime(gensub(/(....)-(..)-(..) (..):(..):(..)/, "\\1 \\2 \\3 \\4 \\5 \\6", 1, ts)) if (ts_epoch >= start_epoch && ts_epoch <= end_epoch) print }' access.log ``` #### 动态生成时间范围(结合 Shell) ```bash #!/bin/bash start_time=$(date -d "1 hour ago" "+%Y-%m-%d %H:%M:%S") end_time=$(date "+%Y-%m-%d %H:%M:%S") awk -v start="$start_time" -v end="$end_time" ' $1" "$2 >= start && $1" "$2 <= end' /var/log/nginx/access.log ``` --- ### 💡 关键要点总结 1. **日志格式要求** - 时间戳需位于行首或固定位置(可通过 `awk -F` 调整分隔符) - 推荐统一使用 `ISO 8601` 格式 (`YYYY-MM-DD HH:MM:SS`) 2. **工具选择建议** | 场景 | 推荐工具 | 原因 | |--------------------|----------|--------------------------| | 精确时间范围 | `awk` | 支持边界判断和复杂逻辑 | | 简单分钟级匹配 | `grep` | 正则简洁高效 | | 连续日志块提取 | `sed` | 模式区间匹配 | 3. **性能优化** ```bash # 发现超出时间范围立即停止读取 awk -v start="..." -v end="..." ' $1" "$2 > end { exit } $1" "$2 >= start { print }' massive.log ``` > **注**:实际使用前需先用 `head` 命令验证日志格式,例如: > `head -n 1 /path/to/logfile | awk '{print $1,$2}'`[^1][^2] --- ### 🔍 相关问题 1. 如何用 awk 处理非标准时间格式(如 `01/Oct/2023:08:00:00`)的日志? 2. 当日志量非常大时(GB 级别),如何优化截取操作的性能? 3. 怎样同时过滤多个时间段的日志(如提取每天高峰期的日志)? 4. 如果日志时间戳存在乱序,应该如何处理? 5. 如何将截取出的日志按小时分割保存到不同文件? [^1]: awk、grep、sed是linux操作文本的三大利器,合称文本三剑客 [^2]: awk命令逐行读取文本,默认以空格或tab键为分隔符进行分隔
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值