如何在shell脚本中检测BSD和GNU版本的日期?

28

我正在编写一个需要进行一些日期字符串操作的Shell脚本。该脚本应在尽可能多的*nix变体上工作,因此我需要处理机器可能具有BSD或GNU版本日期的情况。

什么是检测操作系统类型最优雅的方式,以便我可以发送正确的日期标志?

编辑: 为了澄清,我的目标是使用date的相对日期计算工具,这在BSD和GNU中似乎是不同的。

BSD示例

date -v -1d

GNU 示例

date --date="1 day ago"

2
检测操作系统类型是无用的,因为 GNU date 可能已安装在 BSD 上,或从 GNU-Linux 系统中卸载。 - William Pursell
好的,知道这些对我有用。我应该更清晰地表达我的目标。我的目标是进行相对日期计算,(我想)在GNU中可以使用-v,在BSD中略有不同但使用 --date 可以实现。是否有一种通用的方法来进行这些日期减法运算? - bryan kennedy
4个回答

20

你想检测正在使用的date命令的版本,而不仅仅是操作系统的版本。

GNU Coreutils date命令接受--version选项;其他版本不支持:

if date --version >/dev/null 2>&1 ; then
    echo Using GNU date
else
    echo Not using GNU date
fi

但正如William Pursell所建议的,如果可能的话,您应该只使用两者都共有的功能。

(我认为GNU日期可用的选项基本上是BSD版本可用选项的超集;如果是这种情况,那么假设BSD版本的代码应该可以在GNU版本上工作。)


+1:这是一个不错的方法,但我想再深入些。如果您担心某个特定选项可能不可用,请尝试它:如果失败了,则从传递给日期的选项字符串中删除它。这比仅使用perl更快地变得非常乏味。 - William Pursell
@WilliamPursell:很难想象有什么情况下你想使用某个选项,但是可以在没有它的情况下继续。也许如果您想以某种方式格式化日期,但如果不可用,则可以回退到默认值(仅当输出旨在供人阅读时才有意义)。但这是问题背后的隐含假设,所以... - Keith Thompson
2
BSD包含一些GNU不可用的标志,反之亦然。例如,要从UTC转换,BSD需要-r标志,该标志指向一个文件,而GNU则可以使用-d来完成此任务,但BSD上不存在该标志。也许可以在两者上都进行UTC转换...但我还没有找到它。 - mateor
2
该问题明确提到了“-v”和“--date”,这两个选项与其他版本不共享,并且具有不同的语法。 - Quantum7

13

使用便携式标志。标准可在此处获得 here

对于打印相对日期的特定问题,可能比使用date更容易使用perl:

perl -E 'say scalar localtime(time - 86400)'

请注意,这种解决方案在23或25小时的日子里完全失败,但是有很多Perl解决方案可用于解决该问题。请参阅Perl FAQ。

但是,您肯定可以使用Keith的想法的变体并执行:

if date -v -1d > /dev/null 2>&1; then
  DATE='date -v -1d'
else
  DATE='date --date="1 day ago"'
fi
eval "$DATE"
或者只是
DATE=$(date -v -1d 2> /dev/null) || DATE=$(date --date="1 day ago")

另一个想法是定义一个函数来使用:

if date -v -1d > /dev/null 2>&1; then
    date1d() { date -v -1d; }
else
    date1d() { date --date='1 day ago'; }
fi

1
有趣的是,在讨论可移植标准时,perl -E 'say "blah"' 对我来说不起作用,所以我不得不使用 perl -e 'print "blah\n"' - MarkHu
“-E” 在 5.12 版本中可用,但在 5.8 版本中不可用。(我不太记得它何时开始有效,可能是在 5.10 系列中。) - William Pursell
@Lauri 谢谢!复制粘贴错误猖獗。已编辑。 - William Pursell
那么唯一的可移植标志是“-u”吗?这非常有限。 - Quantum7
PHP也是一个不错的选择,因为strtotime()具有人性化的语法,例如php -r“echo date('Y-m-d', strtotime('-1 day'));” - jchook
显示剩余2条评论

2
建议使用基于特征的检测,而不是基于操作系统或平台的检测。
以下示例代码展示了它的工作原理:
if is-compatible-date-v; then
    date -v -1d
elif is-compatible-date-d; then
    date --date="1 day ago"
fi

function is-compatible () {
    "$@" >/dev/null 2>&1
}

function is-compatible-date-v () {
    is-compatible date -v +1S
}

function is-compatible-date-d () {
    is-compatible date -d '1 second'
}

我需要编写一些shellcode来处理日期,希望代码能够运行在BSD和GNU版本的date上。最终我编写了一组脚本,为BSD和GNU版本的date提供了统一的接口。
例如:执行以下命令将输出一个比2008-10-10晚21天的日期,并且它可以在BSD和GNU版本的date上正常工作。
$ xsh x/date/adjust +21d 2008-10-10
2008-10-31

脚本可以在以下位置找到:

这是一个名为xsh-lib/core的库的一部分。要使用它们,您需要同时拥有xshxsh-lib/core两个仓库,我在下面列出了它们:

希望这可以帮助有相同需求的人。

0

回答你的问题,可能你需要使用"uname -o"命令。在我的情况下,它是"GNU/Linux"。你可以自己决定检测操作系统类型是否有用。


2
BSD的“uname”命令不接受“-o”标志。可移植性并不仅仅是在您的系统上尝试它并假设它会在其他地方起作用。在这种情况下,您可以使用“-s”标志代替。 - Caleb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接