bash - 将 tcpdump 输出写入压缩/gziped 文件

标签 bash debian gzip tcpdump

我想将 tcpdump 的文本输出写入压缩文件。

首先我尝试了最明显的:

# tcpdump -l -i eth0 | gzip -c > test.gz
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C63 packets captured
244 packets received by filter
0 packets dropped by kernel
4 packets dropped by interface

# file test.gz
test.gz: empty
# 

然后我找到了以下针对 Debian 9 (Stretch) 的解决方案:

# tcpdump -l -i eth0 | ( gzip -c > test.gz & )
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C150 packets captured
160 packets received by filter
0 packets dropped by kernel

# file test.gz 
test.gz: gzip compressed data, last modified: Wed May 23 12:56:16 2018, from Unix
# 

这在 Debian 9 (Stretch) 上运行良好,但在 Debian 8 (Jessie) 上运行不佳:

# tcpdump -l -i eth0 | ( gzip -c > test.gz & )
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
tcpdump: Unable to write output: Broken pipe
# 

两个问题:

  1. “显而易见的解决方案”有什么问题?
  2. 如何在 Debian Jessie 中捕获并压缩 tcpdump 输出? (明显的解决方案在那里也不起作用)

谢谢!

最佳答案

发生了什么

解释一下这里发生了什么:

  • Ctrl+C整个进程组发送 SIGINT。这意味着它不仅仅终止 tcpdump ,但也终止 gzip 。 (您尝试的解决方法是通过将内容移至后台进程,从而移出同一进程组来避免这种情况)。
  • 仅当输出到 TTY 时,stdout 默认是行缓冲的;当输出到 FIFO 时,它是 block 缓冲的,只有在足够大的 block 可用时才从左侧进程写入数据,从而提高效率。在许多情况下,您可以只使用 stdbuf -oL或类似的禁用此功能。然而...
  • gzip 本质上无法完全无缓冲地运行。这是因为基于 block 的压缩算法需要将数据收集到 block 中;批量分析该内容;等等。

所以,如果 gziptcpdump同时终止,这意味着无法保证 tcpdump实际上能够刷新其输出缓冲区,然后有 gzipgzip 之前读取、压缩和写入刷新的数据本身从同时接收到的信号中退出。


解决问题

请注意,包含“Interactive”一词的标题下的代码片段用于交互式使用


可靠的交互式解决方法(针对 Bash)

作为万无一失的解决方案,请将 gzip 移至完全在带外,因此当您在 tcpdump 上按 ctrl+c 时,不容易发送 SIGINT命令:

exec 3> >(gzip -c >test.gz)  # Make FD 3 point to gzip
tcpdump -l -i eth0 >&3       # run tcpdump **AS A SEPARATE COMMAND** writing to that fd
exec 3>&-                    # later, after you cancelled tcpdump, close the FD.

可靠的交互式解决方法(适用于任何 POSIX Shell)

同样的事情,但稍微长一点并且不依赖进程替换:

mkfifo test.fifo                            # create a named FIFO
gzip -c <test.fifo >test.gz & gzip_pid="$!" # start gzip, reading from that named FIFO
tcpdump -l -i eth0 >test.fifo               # start tcpdump, writing to that named FIFO
rm test.fifo                                # delete the FIFO when done
wait "$gzip_pid"                            # ...and wait for gzip to exit

请注意 wait会有gzip进程的退出状态,因此您可以确定它是否遇到错误。


可靠的脚本解决方法(适用于任何 POSIX Shell)

如果我们正在运行脚本,那么最好设置一个信号处理程序,以便我们可以显式处理 SIGINT(通过仅杀死 tcpdump):

#!/bin/sh
[ "$#" -gt 0 ] || {
  echo "Usage: ${0##*/} file.tcpdump.gz [tcpdump-args]" >&2
  echo "  Example: ${0##*/} foo.tcpdump.gz -l -i eth0" >&2
  exit 1
}
outfile=$1; shift
fifo=test-$$.fifo # for real code, put this in a unique temporary directory

trap '[ -n "$tcpdump_pid" ] && kill "$tcpdump_pid"' INT
trap 'rm -f -- "$fifo"' EXIT

rm -f -- "$fifo"; mkfifo "$fifo" || exit
gzip -c >"$outfile" <"$fifo" & gzip_pid=$!

# avoid trying to run tcpdump if gzip obviously failed to start
{ [ -n "$gzip_pid" ] && [ "$gzip_pid" -gt 0 ] && kill -0 "$gzip_pid"; } || exit 1

tcpdump "$@" >"$fifo" & tcpdump_pid=$!

# return exit status of tcpdump if it fails, or gzip if tcpdump succeeds
wait "$tcpdump_pid" || wait "$gzip_pid"

关于bash - 将 tcpdump 输出写入压缩/gziped 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50489159/

相关文章:

java - 解压缩大文件(~80 GB)- 使用 Java 或 shell 脚本

json - 使用 Go 解码 gZip json

bash - 带变量的括号扩展?

python - 从 shell 脚本调用 Python 函数

linux - 从 node.js 作为子进程运行 git?

compilation - Apache2 上的 Mono System.Web.Compilation.CompilationException : VBNC30451: Could not resolve the name 'Type'

linux - 使用 zmv 重命名多个文件而不是目录

Apache tomcat (8.0.36) gzip 不工作 我添加了以下代码

node.js 子进程更改目录并运行该进程

linux - 从 shell 脚本运行时 top 和 grep 不输出任何内容