4

我目前正试图弄清楚为什么 shell 脚本每隔一段时间就会在并发日志记录时失败。

我有一个如下的shell函数:

log()
{
   local l_text=$1
   local l_file="/path/to/logs/$(date +%Y%m%d)_script.log"

   local l_line="$(date +'%Y-%m-%d %H:%M:%S') $(hostname -s) ${l_text}"

   echo ${l_line} >> ${l_file}
}

现在每隔一段时间就会失败并出现语法错误:

/path/to/script.sh: command substitution: line 163: syntax error near unexpected token `)'
/path/to/script.sh: command substitution: line 163: `hostname -s) ${l_text}'

问题是,我有多个子进程,每个子进程都想记录并发送陷阱(在此期间也执行记录)。我已经调试了问题并发现,当同时输入该函数三次时会发生这种情况。首先main进程进入,然后child. 在执行完date部分之后l_textmainget 被 a 中断,trap这是由childand in this引起的,它trap试图记录一些东西。Thechild和 thetrap很好地完成了他们的日志记录,但随后main在陷阱之后恢复并尝试执行该hostname部分(假定)并失败并出现此错误。

因此,它似乎main不喜欢在生成$(date +'%Y-%m-%d %H:%M:%S') $(hostname -s) ${l_text}部分日志语句时进入睡眠状态并且不能很好地恢复。我假设这应该可以正常工作,因为我只是使用局部变量和线程安全的输出方法。

这是我在这里遇到的一般并发问题吗?还是这对于 bash 脚本中的陷阱机制非常具体?我知道 C 中 SIGNAL 处理的商品,所以我知道在 SIGNAL 处理程序中只允许某些操作。但是,我不知道在 bash 脚本中处理信号时是否也适用相同的预防措施。我试图找到这方面的文档,但我能找到的所有文档都没有表明脚本中的信号处理存在任何问题。

编辑

这是一个可用于复制问题的实际简单脚本:

#!/bin/bash

log() {
  local text="$(date +'%Y-%m-%d %H:%M:%S') $(hostname -s) $1"
  echo $text >> /dev/null
}

sub_process() {
  while true; do
    log "Thread is running"
    kill -ALRM $$
    sleep 1
  done
}

trap "log 'received ALRM'" ALRM 

sub_process &
sub_process_pid=$!
trap "kill ${sub_process_pid}; exit 0" INT TERM


while true; do
  log "Main is running"
  sleep 1
done

每隔一段时间,这个脚本就会因为第 5 行的语法错误而被杀死。第 5 行是echo $text >> /dev/null,但是由于语法错误还提到了 hostname 命令,类似于我上面发布的那个,我假设有一个 -也有一个错误,实际错误在第 4 行,即local text="$(date +'%Y-%m-%d %H:%M:%S') $(hostname -s) $1".

有人知道如何处理上面的脚本来纠正它吗?我已经尝试将字符串的构造移到一些临时变量中:

log() {
  local thedate=$(date +'%Y-%m-%d %H:%M:%S')
  local thehostname=$(hostname -s)
  local text="${thedate} ${thehostname} $1"
  echo $text >> /dev/null
}

这样,错误出现的频率就会降低,但它仍然存在,因此这不是真正的修复。

4

1 回答 1

1

我会说这绝对是 bash 中的一个错误,我鼓励您将它报告给 bash 开发人员。至少,对于语法正确的代码,您永远不会遇到语法错误。

作为记录,我得到与你相同的结果GNU bash, version 4.2.10(1)-release (x86_64-pc-linux-gnu)

我发现您可以通过不在陷阱处理程序中调用函数来解决此问题。例如更换

trap "log 'received ALRM'" ALRM 

trap "echo $(date +'%Y-%m-%d %H:%M:%S') $(hostname -s) received ALRM" ALRM

使脚本对我稳定。

我知道 C 中 SIGNAL 处理的商品,所以我知道在 SIGNAL 处理程序中只允许某些操作。但是,我不知道在 bash 脚本中处理信号时是否也适用相同的预防措施。

我想你不应该采取特殊的预防措施,但显然在实践中你这样做了。鉴于如果没有函数调用,问题似乎就消失了,我猜测 bash 中的某些东西要么不能重入它应该在的地方,要么失败首先阻止重新进入。

于 2012-05-14T13:15:57.853 回答