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. [转帖]华为Hi 1620 等ARM 服务器版本CPU信息.

    华为ARM服务器恐依赖党政输血续命 一旦制裁立马休克 http://www.sohu.com/a/240833070_99934330 几年前,ARM服务器被业界炒的火热,AMD.高通.Marvell ...

  2. Logrotate还有谁记得它??

    我发现很多人的服务器上都运行着一些诸如每天切分Nginx日志之类的CRON脚本,大家似乎遗忘了Logrotate,争相发明自己的轮子,这真是让人沮丧啊!就好比明明身边躺着现成的性感美女,大家却忙着自娱 ...

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

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

  4. Ubuntu终端命令--查看端口占用及关闭

    1.查看已连接的服务端口 (ESTABLISHED)     netstat-a 2.查看所有的服务端口(LISTEN,ESTABLISHED)     netstat-ap 3.查看指定端口,可以结 ...

  5. 【转】iBatis.Net的SqlMap.config文件

    转自:http://www.xuebuyuan.com/579671.html   iBatis.Net基本的运行环境配置主要由两个文件组成,分别是SqlMap.config和Provider.con ...

  6. 【BZOJ3609】人人尽说江南好(博弈论)

    [BZOJ3609]人人尽说江南好(博弈论) 题面 BZOJ 洛谷 题解 昨天考试的时候,毒瘤出题人出了一个\(noip\)博弈十合一然后他就被阿鲁巴了,因为画面残忍,就不再展开. 这题是他的十合一中 ...

  7. android中的style部分属性值介绍 --zz

    Android平台定义的主题样式: android:theme="@android:style/Theme.Dialog"   将一个Activity显示为对话框模式 •andro ...

  8. bzoj 4028 : [HEOI2015]公约数数列

    之前看了好几次都没什么思路,今天下定决心把这题切了. 观察到$0-x$的gcd最多变化log次,因为它每次变化一定至少要去掉一个质因子,所以我们可以枚举gcd. 因为数据范围比较小,所以想到了分块. ...

  9. 【MST】P2323 [HNOI2006]公路修建问题

    Description 给定 \(n\) 个点 \(m - 1\) 条无向边,每条边有两种边权,贵一点的和便宜一点的.要求至少选择 \(k\) 条贵边使得图联通且花费最大的边权值最小. Input 第 ...

  10. 音视频处理之FFmpeg封装格式20180510

    一.FFMPEG的封装格式转换器(无编解码) 1.封装格式转换 所谓的封装格式转换,就是在AVI,FLV,MKV,MP4这些格式之间转换(对应.avi,.flv,.mkv,.mp4文件). 需要注意的 ...