在 SHELL 脚本里打日志

本文介绍了如何在Shell脚本中实现一个write_log命令,该命令能够自动添加时间戳、脚本文件名和行号到日志中。详细解释了时间戳和文件名的获取方法,以及解决行号问题的巧妙解决方案,通过alias和eval的结合使用。同时,文章指出在使用alias时遇到的一个常见问题,并提供了相应的解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天小弟在重构代码中的一个脚本模块,其中涉及到日志功能。上午花了点儿时间想出了个在 shell 打日志的技巧,觉得值得写一下。

希望要实现的效果是:实现一个 write_log 命令,给一条出错消息作为输入,write_log 记录日志时自动加上 时间戳、脚本文件名和行号。形如:

2010-12-17 19:13:44 [work.sh:24] FATAL: mkdir -p /x.

时间戳、脚本文件名都好获得,但是行号就没那么容易实现了。shell 中的 $LINENO 变量只能展开成当前行的行号,如果把 write_log 实现成函数的话,势必在函数中无法使用 $LINENO。

开始我想了好大一会儿,觉得 eval 能干这个事情。但是如果用 eval 的话,还不如直接把 $LINENO 传给 write_log 函数呢,与我的初衷不是太相符。我拉来同事讨论了一把,也没解决问题。正当我准备放弃了,计划每次传 $LINENO 参数时,忽然想起来,怎么把 alias 给忘了呢?

于是,write_log 的实现就是这个样子了:

function _write_log()
{
  if [ $# -eq 2 ]; then
    if [ -z $LOGFILE ]; then
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0:$1] $2"
    else
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0:$1] $2" >> $LOGFILE
    fi
  elif [ $# -eq 1 ]; then
    if [ -z $LOGFILE ]; then
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0] $1"
    else
      echo "$(date "+%Y-%m-%d %H:%M:%S") [$0] $1" >> $LOGFILE
    fi
  else
    return 1
  fi
}
alias write_log='_write_log $LINENO' # 这里必须使用单引号

存在的问题是:上面这段代码在 bash 里是不工作的,但是用 sh 可以——即使 sh 也是链接到 bash 的。问题出在 alias 上,可以把问题简化成这样,有一个脚本 a.sh:

$ cat a.sh
alias lss='ls -l'
lss /tmp

这个脚本用 /bin/sh 执行是这样的:

$ sh a.sh 
total 8
drwx------ 2 gdm gdm 4096 2010-12-17 19:34 orbit-gdm
drwx------ 2 gdm gdm 4096 2010-12-17 11:04 pulse-PKdhtXMmr18n

用 /bin/bash 执行是这样的:

$ bash a.sh 
a.sh: line 2: lss: command not found

把 bash 随便 link 成一个叫 sh 的链接文件,再执行是类似这样的:

$ ln -s /bin/bash ~/sh
$ ~/sh a.sh 
total 8
drwx------ 2 gdm gdm 4096 2010-12-17 19:34 orbit-gdm
drwx------ 2 gdm gdm 4096 2010-12-17 11:04 pulse-PKdhtXMmr18n

这个问题肯定是有原因的,我不愿意去翻 bash 源代码,也不知道哪里去找答案,所以我放弃了,直接在文件头加上

#!/bin/sh

### 在 Shell 脚本中打印日志的相关方法 在 Shell 脚本中,日志记录是确保脚本执行过程透明化和可追踪的重要手段。以下是几种常见的日志打印方法及其实现方式: #### 1. 基础日志记录 通过简单的 `echo` 命令将信息输出到指定的日志文件中。例如: ```bash log_file="./script.log" echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script started" >> $log_file ``` 这种方法直接将间戳和消息追加到日志文件中[^1]。 #### 2. 使用函数封装日志逻辑 为了提高代码的复用性和可维护性,可以定义一个日志函数来处理日志输出。以下是一个示例: ```bash log_file="./script.log" log() { local level="$1" shift local message="$*" echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" >> $log_file } log INFO "Script execution started" log WARN "Potential issue detected" log ERROR "Critical error occurred" ``` 此函数支持不同级别的日志(如 `INFO`、`WARN`、`ERROR`),并且会自动添加间戳[^4]。 #### 3. 处理日志大小限制 当日志文件过大,可以通过检查文件大小并进行轮转操作来避免占用过多磁盘空间。例如: ```bash max_size=2000000 # 2MB if [ -f "$log_file" ] && [ $(stat --format="%s" "$log_file") -gt $max_size ]; then mv "$log_file" "${log_file}.old" fi ``` 上述代码会在日志文件超过 2MB 将其重命名为 `.old`,然后创建新的日志文件。 #### 4. 结合定任务记录日志 如果需要定期执行脚本并记录日志,可以使用 `cron` 定任务。例如,每两分钟执行一次脚本并将日志输出到指定文件中: ```bash */2 * * * * /path/to/script.sh >> /path/to/logfile.log 2>&1 ``` 这将标准输出和错误输出都重定向到日志文件中[^3]。 #### 5. 快速定位日志内容 对于复杂的日志文件,可以通过编 Shell 脚本来快速检索特定内容。例如: ```bash grep "ERROR" /path/to/logfile.log | tail -n 10 ``` 这将显示最近 10 条包含 `ERROR` 的日志条目[^2]。 #### 6. 动态生成日志文件名 为了区分不同用户的执行记录或不同间点的日志,可以动态生成日志文件名。例如: ```bash user=$(whoami) timestamp=$(date '+%Y%m%d%H%M%S') log_file="./${user}_log_${timestamp}.log" ``` 这样每个用户每次执行都会生成独立的日志文件。 #### 注意事项 - 确保日志文件路径存在且脚本具有入权限。 - 对于敏感信息(如密码),应避免记录到日志中以防止泄露。 - 日志格式应统一,便于后续分析和检索。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值