zoukankan      html  css  js  c++  java
  • Linux Shell管道调用用户定义函数(使shell支持map函数式特性)

    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是一门面向字符串的语言,但是稍微对其封装就可以使其具备一些高级语言的特性,提高开发效率,使得程序可读性更好。

    .

  • 相关阅读:
    php (一)
    php 运算符
    Python 元组
    Python 深拷贝和浅拷贝的区别
    Python 列表
    Python 字符串
    Python 循环控制
    Python 随机数,数学
    bzoj5018 [Snoi2017]英雄联盟
    bzoj5015 [Snoi2017]礼物
  • 原文地址:https://www.cnblogs.com/cc11001100/p/8974895.html
Copyright © 2011-2022 走看看