Tuesday, November 23, 2021

SegmentFault 最新的文章

SegmentFault 最新的文章


Shell 脚本避坑指南(一)

Posted: 21 Nov 2021 07:34 PM PST

大家好,我是张晋涛。

提到 Shell 大家想必不会太陌生,我们通常认为 Shell 是我们和系统交互的接口,执行命令返回输出,比如 bash 、zsh 等。偶尔也会有人把 Shell 和 Terminal(终端)混淆,但这和本文关系不大,暂且略过。

作为一名程序员,我们可能天天都会用到 Shell ,偶尔也会把一些命令组织到一起,写个 Shell 脚本之类的,以便提升我们的工作效率。

然而在看似简单的 Shell 脚本中,可能隐藏着很深的坑。这里我先给出两段简单且相似的 Shell 脚本,大家不妨来看看这两段代码的输出是什么:

#!/bin/bash set -e -u i=0 while [ $i -lt 6 ]; do   echo $i   ((i++)) done

答案是只会输出一个 0 。

#!/bin/bash set -e -u let i=0 while [ $i -lt 6 ]; do   echo $i   ((i++)) done

答案是没有任何输出,直接退出。

如果你能解释清楚上面两段代码输出结果的话, 那大概你可以跳过这篇文章后续的内容了。

我先来分解下这段代码中涉及到的主要知识点。

变量声明

变量声明有很多种办法, 但是其行为却各有不同。

我们必须先有个基础认识: Bash 没有类型系统,所有变量都是 string 。 基于这个原因,如果是让变量进行算术运算时,不能像在其他的编程语言中那样直接写算术运算符。这会让 bash 解释为对 string 的操作,而不是对数字的操作。

直接声明

(MoeLove)➜  ~ foo=1+1 (MoeLove)➜  ~ echo $foo 1+1

直接声明最简单,但正如前面提到的,直接声明会默认当作 string 进行处理,不能在声明时进行算术运算。

declare 声明

(MoeLove)➜  ~ declare foo=1+1 (MoeLove)➜  ~ echo $foo 1+1

除去直接声明变量外,比较常用的方法是用 declare 来声明变量,但默认情况下,其声明的变量都是按 string 处理的,无法进行正常的算术运算。

declare 整数属性

declare 在声明变量的时候,可以通过 -i 参数增加整数属性,当变量被赋值时,将进行算术运算。

(MoeLove)➜  ~ declare -i bar=1+1 (MoeLove)➜  ~ echo $bar 2

但要注意的是,增加整数属性后,如果将字符串赋值给它,则会出现解析失败的情况,即:将值设置为 0:

(MoeLove)➜  ~ bar=test (MoeLove)➜  ~ echo $bar 0

let 声明

另一种办法,我们可以通过 let 命令进行变量的声明,这种方式允许在声明时进行算术运算,同时也支持将其他值赋值给此变量。

(MoeLove)➜  ~ let baz=1+1 (MoeLove)➜  ~ echo $baz 2 (MoeLove)➜  ~ baz=moelove.info (MoeLove)➜  ~ echo $baz moelove.info

while 循环

while list-1; do list-2; done

Bash 中 while 语法就是这样,在 while 关键字后是一个序列(list),可以是一个或多个表达式/语句,

需要注意的是,当 list-1 返回值为 0 时, list-2 总是会被执行,并且 while 语句最后的返回值是 list-2 最后一次执行的返回值,或者,如果没执行任何语句的话,则返回 0 。

bash 中的算数计算

这部分的内容大家想必常会用到。我来介绍几种常用的方法:

算术扩展

Bash 中的扩展一共有 7 种,算术扩展只是其中之一。具体而言就是通过类似 $((expression)) 这样的形式,来计算表达式的值。例如:

(MoeLove)➜  ~ echo $((3+7)) 10 (MoeLove)➜  ~ x=3;y=7 (MoeLove)➜  ~ echo $((x+y)) 10

expr 命令

expr 是 coreutils 软件包提供的一个命令,可对表达式进行计算,或者比较大小之类的。

(MoeLove)➜  ~ x=3;y=7 (MoeLove)➜  ~ expr $x + $y 10 # 比较大小 (MoeLove)➜  ~ expr 2 \< 3 1 (MoeLove)➜  ~ expr 2 \< 1 0

bc 命令

按定义来说,bc 其实是一种支持任意精度和可交互执行的计算语言。它比上述提到的 expr 要强大的多,尤其是它还支持浮点数运算。例如:

一般浮点数计算

(MoeLove)➜  ~ echo "scale=2;7/3"|bc 2.33 (MoeLove)➜  ~ echo "7/3"|bc 2

注意: scale 需要手动指定,它表示小数点后的位数。默认情况下 scale 的值为 0 。

内置函数

bc 还有一些内置函数,可以方便我们进行一些快速的计算,比如可以利用 sqrt() 快速的计算平方根。

(MoeLove)➜  ~ echo "scale=2;sqrt(9)" |bc 3.00 (MoeLove)➜  ~ echo "scale=2;sqrt(6)" |bc 2.44

脚本

此外, bc 还支持一种简单的语法,可以支持声明变量,编写循环和判断语句等。例如:我们可以打印20 以内可以被 3 整除的数:

(MoeLove)➜  ~ echo "for(i=1; i<=20; i++) {if (i % 3 == 0) i;}" |bc 3 6 9 12 15 18

bash 的调试

其实 bash shell 中并没有内置调试器。很多情况下,都是采用重复运行加打印来进行调试。但这种方式不够高效。

这里介绍一种比较直观的,也比较方便的用来调试 shell 代码的办法。以下是一段示例 shell 代码。

(MoeLove)➜  ~ cat compare.sh  #!/bin/bash read -p "请输入任意数字: " val real_val=66 if [ "$val" -gt "$real_val" ] then    echo "输入值大于等于预设值" else    echo "输入值比预设值小" fi

为其增加执行权限,或者使用 bash 执行:

(MoeLove)➜  ~ bash compare.sh  请输入任意数字: 33 输入值比预设值小

详细模式

通过增加 -v 选项,即可开启详细模式,用于查看所执行的命令。当然,我们也可以通过在 shebang 上直接增加 -v 选项, 或者增加 set -v 来开启此模式

(MoeLove)➜  ~ bash -v compare.sh which () {  ( alias;  eval ${which_declare} ) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@" } #!/bin/bash read -p "请输入任意数字: " val 请输入任意数字: 33 real_val=66 if [ "$val" -gt "$real_val" ] then    echo "输入值大于等于预设值" else    echo "输入值比预设值小" fi 输入值比预设值小

使用 xtrace 模式

我们可以通过增加 -x 参数来进入 xtrace 模式,用于调试执行阶段的变量值。

(MoeLove)➜  ~ bash -x compare.sh + read -p '请输入任意数字: ' val 请输入任意数字: 33 + real_val=66 + '[' 33 -gt 66 ']' + echo 输入值比预设值小 输入值比预设值小

识别未定义变量

以下示例中,我故意写错一个字符。执行脚本后,你会发现没有任何报错,但结果并不是我们预期的。这类可能是手误居多,所以我们需要检查是否存在未绑定的变量。

(MoeLove)➜  ~ cat add.sh  #!/bin/bash five=5 ten=10 total=$((five+tne)) echo $total (MoeLove)➜  ~ bash add.sh 5 (MoeLove)➜  ~ bash -u add.sh add.sh: line 4: tne: unbound variable

增加 -u 选项, 可以检查变量是否未定义/绑定。

组合使用

以上是几种比较常见的使用方式,当然,也可以把它进行组合使用。比如上面的变量未定义的问题, 组合使用 -vu 就可以直接看到具体出现问题的代码是什么内容了。

(MoeLove)➜  ~ bash -vu add.sh which () {  ( alias;  eval ${which_declare} ) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@" } #!/bin/bash five=5 ten=10 total=$((five+tne)) add.sh: line 4: tne: unbound variable

将调试信息输出到指定文件

这里我打开了一个特定 FD 上的 debug.log 文件,注意这个 FD 需要与 BASH_XTRACEFD 配置的一致,另外我修改了 PS4 的变量内容,它的默认值是 + 看起来会比较乱,而且没有有效信息,我通过设置 PS4='$LINENO: ' 让它显示行号。

然后在需要调试的位置设置 set -x ,在结束的未知设置 set +x ,这样调试日志中就只会记录我需要调试部分的日志了。

(MoeLove)➜  ~ cat compare.sh  #!/bin/bash exec 6> debug.log  PS4='$LINENO: '  BASH_XTRACEFD="6"  read -p "请输入任意数字: " val real_val=66 set -x if [ "$val" -gt "$real_val" ] then    echo "输入值大于等于预设值" else    echo "输入值比预设值小" fi set +x  echo "End" (MoeLove)➜  ~ bash compare.sh  请输入任意数字: 88 输入值大于等于预设值 End (MoeLove)➜  ~ cat debug.log  8: '[' 88 -gt 66 ']' 10: echo $'\350\276\223\345\205\245\345\200\274\345\244\247\344\272\216\347\255\211\344\272\216\351\242\204\350\256\276\345\200\274' 14: set +x

这里介绍了通过 set 设置选项 的方式较简单,其他的比如使用 trap 加调试的方式也推荐大家去尝试下,这里就不展开了。

回到开始的问题

那我们用刚从介绍的调试方法来执行下开头的两个脚本,并且进行问题的解答。

第一个

(MoeLove)➜  ~ bash -xv demo1.sh #!/bin/bash set -e -u + set -e -u i=0 + i=0 while [ $i -lt 6 ]; do   echo $i   ((i++)) done + '[' 0 -lt 6 ']' + echo 0 0 + (( i++ ))

从上述调试结果可以看到,这个脚本在输出 0 然后执行完 ((i++)) 后退出。为什么呢? 主要是由于在脚本顶部增加的 set -e 选项。

该选项在遇到首个非 0 值的时候会直接退出。 我们来解释下:

(MoeLove)➜  ~ i=0 (MoeLove)➜  ~ $((i++)) (MoeLove)➜  ~ echo $? 1

可以看到,执行 ((i++)) 后,返回值其实是 1 ,所以触发了 set -e 的退出条件,脚本便退出了。

第二个

(MoeLove)➜  ~ bash -xv demo2.sh #!/bin/bash set -e -u + set -e -u let i=0 + let i=0

第二个和第一个的最主要区别在于变量的赋值上, let i=0 的返回值是 1 ,所以也就会触发 set -e 的退出条件了。我们尝试将第二个脚本修改下,再次执行:

[tao@moelove ~]$ cat demo2-1.sh #!/bin/bash set -e -u let i=1 while [ $i -lt 6 ]; do   echo $i   ((i++)) done  [tao@moelove ~]$ bash demo2-1.sh  1 2 3 4 5

let i=0 修改成 let i=1 即可按预期执行成功。

总结

本篇中,我们主要聊了 bash shell 中的变量声明,循环,数学运算以及 bash shell 的调试。是否对你有所启发呢? 欢迎留言进行交流。

  • 注:本文仅讨论 Bash Shell

欢迎订阅我的文章公众号【MoeLove】

数字化时代,纯前端表格控件或将成为协同办公的解决方案

Posted: 22 Nov 2021 10:28 PM PST

毫无疑问,人类社会的发展进程已经步入到数字化时代,数字化变革成为了各行各业的关注焦点。据IDC发布的《未来企业效率白皮书》,到2022年,全球GDP的60%以上都将是数字化,每个行业的增长都将由数字化增强的产品、运营和关系驱动。而数字经济的运转模式和商业模式,也必将给传统工业社会以来所形成的工作方式带来转变。

可以确定的是,数字经济需要一种更为高效和灵活的工作方式,而协同办公将成为很多企业的办公常态。从会议预约到采购入库,从销售报表到调查统计……包括日常办公、资产管理以及业务管理在内的诸多场景都需要在技术的帮助下实现数字化、智能化,这无疑需要大量的开发工作。

协同办公,EXCEL的办公"盲区"

而在大数据时代,数据对于任何行业的企业而言都至关重要,数据资产也成为企业在数字化时代最重要的资产之一。然而只要你进行数据处理、数据分析、数据透视等工作,就不可避免地接触过电子表格。这其中,尤以EXCEL的市场占有率和知名度最高,是企业不可或缺的办公"助手"。

虽然EXCEL功能强大,给工业化时代的个人办公和企业办公带来了极大便利。但在移动互联的数字化时代,它有个致命缺点:即当涉及权限管理、工作流、协同办公时,就进入了EXCEL的办公盲区。

当企业对信息化要求越来越高,以EXCEL为代表的单机办公软件就越来越难以适应未来发展,其在共享交互、工作流、协同办公、用户权限控制上更显得尤为鸡肋,这也成为困扰广大企业和开发者的痛点难题之一。

数据需求多+开发周期长,开发人员工作量骤升

除了难以实现协同办公,面对表格开发这一需求,当前大部分中小型公司的解决办法是:将业务数据储存在数据库中,待需要数据时IT人员通过sql语句取数,然后再导出Excel表格给业务人员,然后业务人员再在Excel中处理数据。

但是在这一工作流程会出现很多问题:

首先,数据及时性无法保证。Excel里面的数据都保存在各个业务人员的电脑中,不管是当月汇总还是当日汇总,都存在一定的时间差,无法做到在线实时更新。

其次,存在重复性劳动。总部型的大企业通常下属部门较多,分支机构的很多表格样式都是一样的。但因为采用Excel各自统计和汇总数据,于是产生了大量的重复劳动,效率低下。

此外,还有一个角色权限的问题。Excel提供了有限的安全性,它只能提供限制用户访问和修改的权限,但是无法对用户进行角色的管理,也不能对数据进行级别的访问限制,企业的数据安全无法保证。

面对这一冗长的工作流程和大量的表格开发需求,如果以传统方式去开发应用程序,每一步都要通过编写代码来实现,就不得不面临着开发周期长、业务响应不及时、效率低等问题,而且后期维护也需要专业的开发人员来负责,进一步加大了开发人员的工作量。

从员工的数字化办公到企业的数字化转型,到底什么样的工具能够满足表格开发人员开发效率高+易运维的需求,帮助使用者以直观、简洁的方式理解业务数据呢?兼具开放性和扩展性的纯前端表格控件或许提供了解决方案。

类EXCEL纯前端表格控件或将成为开发者的"刚需"

和前端开发一样,在数字化时代,表格开发也面临着高频多变的需求。如何高效高质地完成这些需求是开发人员的关注焦点之一,而控件产品或许是个不错的选择。

因为控件是对数据和方法的封装。这类工具封装了大量的基础功能,且支持复用、可以与其他对象进行交互,是一种减少重复工作、提升开发效率的利器,能够为项目开发和软件交付提供便利。这一点在SpreadJS上体现的淋漓尽致。

SpreadJS 使用稀疏数组作为存储结构,用 HTML5 Canvas 绘制交互界面,内置 32 种图表、18 种迷你图和 182 种形状,提供了丰富的数据可视化手段和高效的计算引擎,并针对数据处理性能进行了优化,满足了企业各类数据计算、可视化、数据透视分析等需求,最大化节省了存储空间。让业务人员直观简洁地获取并理解数据成为可能。

除了在视觉端的突出表现,SpreadJS 的计算引擎还支持 450 多种 Excel 公式函数,包括自定义函数、数组函数、动态数组、异步函数、XMATCH、LET 、XLOOKUP 函数等,开发者可以自定义、跨表格引用、异步调用等多场景计算需求,实现数据聚合,让有效数据不断完善。

而针对开发者的另一大痛点"协同编辑",纯前端表格控件同样拥有良好的表现。SpreadJS 提供了单元格级别的操作颗粒度。通过开发者的二次开发,即可在前端解析 Excel 文档,让多人协作、协同编辑、数据同步、版本管理以及历史查询在线文档成为可能,极大地提高了数据的更新频率,告别低效繁冗的单机时代。

不同企业、不同开发者惯用的应用不一,要想实现高效开发和便于运维这两个"KPI",兼容性和扩展性就是开发者必须要考量的另一大关键指标。

作为一款纯前端控件,SpreadJS 支持以原生的方式嵌入各类应用,无需借助后台代码和第三方组件,即可以与各类后端技术框架相结合,从而实现跨平台开发。其表格编辑器还提供了"神似" Excel 的功能,内置在线填报、打印报送、实时预览和数据校验的 API;开发者可在线/离线设计报表模板、编辑、计算、分析数据并与数据库绑定,加载并修改各种 Excel 文档,并将修改后的数据保存到数据库中。

此外,这款纯前端控件内置 了18 种条件格式、32 种图表、53 项单元格格式和 182 种形状,兼容 Excel 数据格式,可向 Web 系统中嵌入 Excel 功能,提供高度类似 Excel 的使用体验。通过二次开发,可将 SpreadJS 嵌入企业报表 SaaS 平台,复用业务系统原始 Excel 报表模板,可直接在浏览器中完成 Excel、CSV、JSON 等文件的导入导出、PDF导出、打印及预览操作,从而降低从本地到线上的数据迁移工作量。

总结起来,这款基于 HTML5 的纯前端表格控件,具备"高性能、跨平台、与 Excel 高度兼容"的特性,这恰恰与表格开发者们所需要的"高效开发、便于运维、多人协同"的需求高度"适配"。

结语

不难看出,以SpreadJS为代表的的纯前端表格控件对于效率的提升,能力的解放是显著的,其本质就是提质增效。一方面是对开发人员的提效,在降低企业研发成本和项目交付风险的同时,减少开发人员的工作量,提升工作效率。另一方面是对使用者的提效,利用高度可视和类excel的表格控件,让多人在线协同进行数据维护和分析成为可能,大大降低了后期维护的难点。在这个效率至上的数字化时代,提高开发效率仍然是重中之重。从这个角度来看,纯前端开发控件不失为一个好的选择。

或许当使用诸如SpreadJS这类前端控件后,所需要的开发者少了,但对于开发者的要求却更高了。不仅要对业务非常了解,还要对数据和开发理解的非常通透,最后串联起从需求到开发的整个过程。

No comments:

Post a Comment