我目前正在编写一个脚本来存档几个日志文件,并希望将它们合并到一个存档中,该存档根据其中一个日志文件(即access.log)中第一行和最后一行的日期和时间命名。
但就我而言,我无法弄清楚如何从这些行中获取这些信息并将其组合成文件名。
有问题的行来自 apache.log 文件,我可以使用head
和简单地获取它tail
:
例子:
$ head -n1 /home/server/log/access.log.1
84.1.11.243 - - [21/Jan/2017:14:53:49 +0000] "GET /index.php/2016/05/26/tutorial-how-to-install-ubuntu-and-other-debian-based-distributions-via-debootstrap/ HTTP/1.1" 200 18413 "https://www.google.hu/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"
$ tail -n1 /home/server/log/access.log.1
71.3.17.120 - - [20/Dec/2017:16:17:50 +0000] "POST / HTTP/1.1" 200 27639 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; LCTE; rv:11.0) like Gecko"
预期的文件名结果应该包括这些消息的日期和时间戳。
我想使用示例行,但可以根据答案结果进行更改:
tar -caf "backup-logfiles-$start-til-$end.tar.gz" access.log error.log ftp.log
欢迎使用任何解决方案将该值提取到$start
和中$end
。
答案1
下面是一个非常复杂的 shell 单行代码(使用你在聊天中提到):
$ name=$(printf 'backup-logfiles-%s-til-%s' $(date -d "$(head -n1 logfile | grep -oP '\[\K\S+' | sed 's|/| |g; s/:/ /')" +%Y-%m-%d-%H:%M:%S) $(date -d "$(tail -n1 logfile | grep -oP '\[\K\S+' | sed 's|/| |g; s/:/ /')" +%Y-%m-%d-%H:%M:%S))
$ echo $name
logfiles-2017-01-21-14:53:49-til-2017-12-20-16:17:50
要分别获取开始和结束变量,请执行以下操作:
$ start=$(head -n1 logfile | grep -oP '\[\K\S+' | sed 's|/|-|g; s/:/ /')
$ end=$(tail -n1 logfile | grep -oP '\[\K\S+' | sed 's|/|-|g; s/:/ /')
$ echo "backup-logfiles-$start-til-$end.tar.gz"
backup-logfiles-21-Jan-2017 14:53:49-til-20-Dec-2017 16:17:50.tar.gz
或者,如果您想要一个数字日期:
$ start=$(date -d "$(head -n1 logfile | grep -oP '\[\K\S+' | sed 's|/|-|g; s/:/ /')" +%Y-%m-%d-%H:%M:%S)
$ end=$(date -d "$(tail -n1 logfile | grep -oP '\[\K\S+' | sed 's|/|-|g; s/:/ /')" +%Y-%m-%d-%H:%M:%S)
$ echo "backup-logfiles-$start-til-$end.tar.gz"
backup-logfiles-2017-01-21-14:53:49-til-2017-12-20-16:17:50.tar.gz
答案2
仅使用sed
,仅用于乐趣赢得高尔夫比赛 ;)
name=$(sed -rn 's|/|-|g;s/.* \[([^ ]+) .*/\1/;1p;$p' file | sed 'N;s/\n/-til-/')
$ echo $name
21-Jan-2017:14:53:49-til-20-Dec-2017:16:17:50
但是如果您想将此文件传递给tar
,冒号可能会导致问题:
An archive name that has a colon in it specifies a file or device on a remote
machine. The part before the colon is taken as the machine name or IP address,
and the part after it as the file or device pathname, e.g.:
--file=remotehost:/dev/sr0
您可以通过传递一个选项来解决这个问题:
--force-local
Archive file is local even if it has a colon.
但是这里有一个用更多连字符替换冒号的命令:
name=$(sed -rn 's|[/:]|-|g;s/.* \[([^ ]+) .*/\1/;1p;$p' file | sed 'N;s/\n/-til-/')
我们可以使用交替来代替字符类并节省一个字节:)
name=$(sed -rn 's#/|:#-#g;s/.* \[([^ ]+) .*/\1/;1p;$p' file | sed 'N;s/\n/-til-/')
笔记
-r
使用 ERE-n
在我们要求之前不要打印任何内容s|/|-|g
用 替换所有/
字符-
(因为我们不能使用 文件名/
)s|[/:]|-|g
/
用连字符替换:
所有位置的字符。s#/|:#-#g
替换/
或:
用-
所有地方;
单独的sed
命令s/.* \[([^ ]+) .*/\1/
捕获方括号内(从第一个[
到第一个空格)的日期和时间。1p;$p
仅打印第一行和最后一行|
将其通过管道输送到另一个sed
(呃!)N
将两行读入模式空间......s/\n/-til-/
...所以我们可以用以下方法替换换行符-til-