Linux中有一个管道的概念,常用来流式的处理文本内容,比如一个文件对其中的每一行应用好几个操作,出于两个方面的考虑可能需要在管道中使用用户定义函数:

1. 刚需: 内置的sed/awk之类的可能没法满足我们的需求,只能使用用户定义函数

2. 代码质量: 如果是流式操作很多很长,那么可能就需要将其进行拆分,将相关的部分封装为一个函数,然后流式调用函数,这样程序的可读性更好,也更容易维护

在管道中上一个程序的标准输出会被放到下一个程序的标准输入,处在管道中的程序需要做的就是读取标准输入中的东西进行处理。

下面是一个在管道中使用用户定义函数的例子,print函数不断地从标准输入流中读取数据然后放到标准输出流中:

#! /bin/bash

print(){
while read line
do
echo "$line"
done
} cat original.data | print

上面的例子是比较简单的,那么现在需求又改了,假设读入的每行都是一个数字,需要对数字加1并保存到一个新的文件,那么对上面的脚本进行扩充:

#! /bin/bash

add(){
while read line
do
echo $(($line+1))
done
} save(){
while read line
do
echo $line >> result.data
done
} print(){
while read line
do
echo "$line"
done
} # cat original.data | print
cat original.data | add | save

上面的脚本虽然功能实现了,但是有冗余代码,在函数add和save中重复了循环读取输入流数据的代码,现在重构一下,将重复读取的部分抽象出来为一个新的函数map,此函数接受一个可以处理单行的函数的名字function_name,map函数每读取一行就调用function_name函数,将读取到的内容作为参数传入,如果处理单行的函数function_name还有输出,则还可以作为下一个管道命令的输入,这个处理模型类似于函数式编程中的map的概念,即这里通过一个小技巧使得shell支持部分函数式的功能。

现在按照上面的思想来对脚本进行改造:

#! /bin/bash

add(){
echo $(($1+1))
} save(){
echo $1 >> result.data
} print(){
echo "$1"
} map(){
function_name=$1
while read line
do
$function_name "$line"
done
} # cat original.data | print
cat original.data | map "add" | map "save"

现在看起来好多了,并且程序的可读性也得到了保障,但是如果我想对add传入一个参数指定究竟要加几怎么办呢,又或者我想在save函数自定义保存到位置又该怎么办呢?

这个很简单,在map中取完第一个参数之后就没再用了,直接shift参数然后直接传递即可,再重构一下代码:

#! /bin/bash

add(){
echo $(($1+$2))
} save(){
echo $1 >> $2
} print(){
echo "$1"
} map(){
function_name=$1
shift
while read line
do
$function_name "$line" $@
done
} # cat original.data | print
cat original.data | map "add" 2 | map "save" "result.data"

看起来似乎很完美了,但还能做得更好吗?

如果map方法需要在其它地方调用怎么办?一种方法是将map抽取为utils.sh或者functional.sh之类的库,然后在使用的时候引入它即可,比如新建functional.sh的文件:

##################################################
#
#
# shell 函数式库
#
#
################################################## # $1 函数名
# $[2,] 传递给$1函数的参数
map(){
function_name=$1
shift
while read line
do
$function_name "$line" $@
done
}

使用的时候source functional.sh即可使用map函数。

或者更细粒度的,直接将map抽取为一个文件(以后如果有其它的类似函数也都会抽取为单独的文件),并且放到PATH中,这样使用的时候无需引入,直接就可以使用,比如创建一个文件叫做map:

##################################################
#
#
# functional.map
#
#
################################################## # $1 函数名
# $[2,] 传递给$1函数的参数
map(){
function_name=$1
shift
while read line
do
$function_name "$line" $@
done
} map #@

但是这种方式有个陷阱就是一定要记得每次使用时保证map在$PATH中。

上面就是一步一步优化封装了一个小小的map函数使得shell支持map特性,虽然shell是一门面向字符串的语言,但是稍微对其封装就可以使其具备一些高级语言的特性,提高开发效率,使得程序可读性更好。

.

Linux Shell管道调用用户定义函数(使shell支持map函数式特性)的更多相关文章

  1. 应用C#和SQLCLR编写SQL Server用户定义函数

    摘要: 文档阐述使用C#和SQLCLR为SQL Server编写用户定义函数,并演示用户定义函数在T-SQL中的应用.文档中实现的 Base64 编码解码函数和正则表达式函数属于标量值函数,字符串分割 ...

  2. 【翻译】Flink Table Api & SQL — 用户定义函数

    本文翻译自官网:User-defined Functions  https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/tabl ...

  3. SQL——用户定义函数

    根据用户定义函数返回值的类型,可将用户定义函数分为如下三个类别: (1) 返回值为可更新表的函数 若用户定义函数包含单个 SELECT 语句且该语句可更新,则该函数返回的表也可更新,这样的函数称为内嵌 ...

  4. SQL 中用户定义函数的使用方法

    --用户定义函数的分类: /* 1.标量函数 2.表值函数 2.1内联表值函数  返回单个SELECT语句, 它没有相关的返回变量和函数体 2.2多语句表值函数  是视图和存储过程的结合 可嵌套 */ ...

  5. SqlServer——用户定义函数

    根据用户定义函数返回值的类型,可将用户定义函数分为如下三个类别: (1) 返回值为可更新表的函数 若用户定义函数包含单个 SELECT 语句且该语句可更新,则该函数返回的表也可更新,这样的函数称为内嵌 ...

  6. SQL Server 2019 中标量用户定义函数性能的改进

    在SQL Server中,我们通常使用用户定义的函数来编写SQL查询.UDF接受参数并将结果作为输出返回.我们可以在编程代码中使用这些UDF,并且可以快速编写查询.我们可以独立于任何其他编程代码来修改 ...

  7. 调试SQL Server的存储过程及用户定义函数

    分类: 数据库管理 2005-06-03 13:57 9837人阅读 评论(5) 收藏 举报 sql server存储vb.net服务器sql语言 1.在查询分析器中调试 查询分析器中调试的步骤如下: ...

  8. Hadoop Hive概念学习系列之hive里的用户定义函数UDF(十七)

    Hive可以通过实现用户定义函数(User-Defined Functions,UDF)进行扩展(事实上,大多数Hive功能都是通过扩展UDF实现的).想要开发UDF程序,需要继承org.apache ...

  9. linux shell 管道命令(pipe)使用及与shell重定向区别

    管道命令操作符是:”|”,它仅能处理经由前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于 stdandarderror 信息没有直接处理能力.然后,传递给下一个命令 ...

随机推荐

  1. [知乎]老狼:深入PCI与PCIe之二:软件篇

    深入PCI与PCIe之二:软件篇 https://zhuanlan.zhihu.com/p/26244141 我们前一篇文章(深入PCI与PCIe之一:硬件篇 - 知乎专栏)介绍了PCI和PCIe的硬 ...

  2. [转帖]PCI-E的部分知识

    PCI-E PCI-E全称是PCI Express,是新一代的总线接口.采用了目前业内流行的点对点串行连接,比起PCI以及更早期的计算机总线的共享并行架构,每个设备都有自己的专用连接,不需要向整个总线 ...

  3. Struts1 工作流程

    一个老项目的维护 , 需要学习一下 Struts1. struts1运行步骤 1.项目初始化:项目启动时加载 web.xml,struts1 的总控制器 ActionServlet 是一个 Servl ...

  4. 我项目中使用userData的实例 UserData.js

    关于userData的介绍,请参见http://hi.baidu.com/kaisep/blog/item/1da9a3312d2da5a15edf0e87.htmlhttp://hi.baidu.c ...

  5. Android Apollo MQTT入门

    一.Apache Apollo服务器其实是一个消息中转站 下载地址 http://activemq.apache.org/apollo/download.html 服务搭建方式,参看博客Android ...

  6. 安装pycharm软件后,打开robot framework怎么默认用pycharm打开

    1.打开ride.py的属性,修改打开方式

  7. Redis 基础:Redis 事件处理

    Redis 事件处理 Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件: 文件事件(file event):Redis服务器通过套接字与客户端(或其他Redis服务器)进行连接,而文件事 ...

  8. MT【116】三个点动起来

    评:当若干个变量时抓住主变量,立体几何问题平面化.

  9. 利用java实现可远程执行linux命令的小工具

    在linux的脚本中,如果不对机器做其他的处理,不能实现在linux的机器上执行命令.为了解决这个问题,写了个小工具来解决这个问题. 后面的代码是利用java实现的可远程执行linux命令的小工具,代 ...

  10. 毕业设计预习:maxplus2入门教程

    maxplus2入门教程 一.安装配置(maxplus2.zip) 下载安装完成后,运行maxstart.exe,显示如下错误提示: 为节省配置工作,在E:盘下新建maxplus2文件夹,仅将所需附加 ...