unix时间戳-你或许不知道,shell不能比较大小

生活百科1年前 (2023)发布 aixure
66 0 0

“究竟在干什么”是一系列关于软件开发过程中背后运作原理的文章,每一篇文章旨在讲解一些在日常编程实践中常见但可能并不为人所熟知的技术细节,抛砖引玉,期待激发读者朋友的更多思考。

如何在 shell 中比较大小?

如果在搜索引擎中搜索“shell 比较”,那么得到的结果基本上都在告诉你要写[ blablabla ]这样的代码

例如,如果想知道当前的 UNIX 时间是否已经以16开头,可以用下列的 shell 代码

#!/bin/bash
ts=$(date '+%s')
if [ "${ts}" -gt 1600000000 -a "${ts}" -lt 1700000000 ]; then
    echo '当前的UNIX时间戳已经以16开头啦。'
else
    echo '当前的UNIX时间戳还没以16开头哦。'
fi

当我写这个的时候,date '+%s'的值为1587901648,所以运行后走的是else的分支。

[是 shell 的语法么?

大部分写 shell 代码的人或许会认为,[]是 shell 语言用于实现一系列的比较操作的特殊语法。但实际上,[]并不是一个语法——[是一个独立的命令行程序,]则什么都不是,仅仅是一个普通的字符。

在 bash 中使用which命令可以看到[的真面目

[是一个独立的程序,对unix时间戳,你没有看错(鲍尔默脸)。而且[有它自己的 man 文档

unix时间戳-你或许不知道,shell不能比较大小

在 man 文档中出现了另外一个命令test,它和[的功能是一模一样的。或许test是一个“yet another [”?真相却更简单一点——test和[是同一个东西

unix时间戳-你或许不知道,shell不能比较大小

[源代码的二三事

可以在 GitHub 上找到[和test的源代码[1],代码很短,稍微读一下可以发现不少有意思的地方。

众所周知,如果在 shell 代码中使用[做比较运算,必须写上对应的右方括号]。但既然[是一个普通的外部程序,那么这个匹配括号的检查显然不会是 shell 来做的——没错,[自己会检查是否有写上相应的右方括号,这一段逻辑在源文件的main函数开始不久就出现了。

unix时间戳-你或许不知道,shell不能比较大小

这个检查只有在程序被以[的名字启动的时候才会生效,所以test 1 -eq 1是不需要写括号的。

其实除了上文中给出的那些比较和测试运算符之外,[也支持复杂的逻辑运算表达式,比如文章开头的示例代码中的-a就是逻辑与的意思。在代码的注释中还贴心地给出了所接受的参数的 BNF

unix时间戳-你或许不知道,shell不能比较大小

而解析参数的过程则是一个手写的递归下降语法分析器,在源代码中可以找到与上面的产生式对应的多个函数:oexpr、aexpr、nexpr、primary,以及binop。

由于在 shell 语言中,0 表示逻辑真,而 1 表示逻辑假(与 C 语言相反),所以在main函数中,如果发现传入的第一个参数为感叹号(!,表示逻辑取反),则将oexpr的调用结果直接返回,否则需要将结果取反后再从main函数中返回——给操作系统。

shell 真的不原生支持比较?

尽管在 bash 中unix时间戳,[的确是作为一个外部程序存在的,但在 zsh 中却相反

unix时间戳-你或许不知道,shell不能比较大小

而且,即使是 bash 也并非完全没有原生的比较操作——此处需要召唤[[。[[是 shell 的保留字,它是一个less suprise版本的[,在 Stack Overflow 上有不少关于它的问答值得一看:

第二个链接的回答中还给出了一个值得一看的、关于 bash 中的“测试”功能的指引[2],其中甚至提到了

It can produce surprising results, especially for people starting shell scripting that think [ ] is part of the shell syntax.

后记

不得不承认,本文标题党了一把,shell 还是自身就具备比较大小这样的功能的。

参考资料

[1]

和test`的[源代码:

[2]

指引: #Bash_Tests

限时特惠:本站每日持续更新海量各大内部网赚创业教程,会员可以下载全站资源点击查看详情
站长微信:

© 版权声明

相关文章

暂无评论

暂无评论...