如果xargs是map,那么什么是过滤器?

问题描述 投票:12回答:4

我认为xargs是UNIX shell的map函数。什么是filter功能?

编辑:看起来我必须更明确一点。

假设我必须提交一个接受单个字符串作为参数的程序,并返回退出代码0或1.该程序将作为它接受的字符串的谓词。

例如,我可能决定将字符串参数解释为文件路径,并将谓词定义为“此文件是否存在”。在这种情况下,程序可以是test -f,给定一个字符串,如果文件存在则退出0,否则退出1。

我还必须交出一串串。例如,我可能有一个文件~/paths包含

/etc/apache2/apache2.conf
/foo/bar/baz
/etc/hosts

现在,我想创建一个新文件~/existing_paths,它只包含我文件系统中存在的那些路径。就我而言,那就是

/etc/apache2/apache2.conf
/etc/hosts

我想通过读取~/paths文件,通过谓词test -f过滤这些行,并将输出写入~/existing_paths来做到这一点。与xargs类比,这看起来像:

cat ~/paths | xfilter test -f > ~/existing_paths

这是我正在寻找的假设程序xfilter

xfilter COMMAND [ARG]...

其中,对于其标准输入的每行L,将调用COMMAND [ARG]... L,如果退出代码为0,则打印L,否则它不会打印任何内容。

要明确,我不是在寻找:

  • 一种过滤存在的文件路径列表的方法。这是一个具体的例子。
  • 怎么写这样的程序。我能做到。

我正在寻找:

  • 一个预先存在的实现,如xargs,或
  • 明确解释为什么不存在这一点
shell map filter xargs
4个回答
6
投票

如果地图是xargs,过滤器是......仍然是xargs

示例:列出当前目录中的文件并过滤掉非可执行文件:

ls | xargs -I{} sh -c "test -x '{}' && echo '{}'"

这可以通过(非生产就绪)功能方便:

xfilter() {
    xargs -I{} sh -c "$* '{}' && echo '{}'"
}
ls | xfilter test -x

或者,您可以通过GNU Parallel使用并行过滤器实现:

ls | parallel "test -x '{}' && echo '{}'"

5
投票

所以,你正在寻找:

 reduce(  compare(  filter( map(.. list()) ) ) )

什么可以改写为

 list | map | filter | compare | reduce

bash的主要力量是流水线,因此不需要有一个特殊的filter和/或reduce命令。事实上,几乎所有unix命令都可以在一个(或多个)函数中起作用:

  • 名单
  • 地图
  • 过滤
  • 降低

想像:

find mydir -type f -print | xargs grep -H '^[0-9]*$' | cut -d: -f 2 | sort -nr | head  -1
^------list+filter------^   ^--------map-----------^   ^--filter--^   ^compare^  ^reduce^

创建测试用例:

mkdir ./testcase
cd ./testcase || exit 1
for i in {1..10}
do
    strings -1 < /dev/random | head -1000 > file.$i.txt
done
mkdir emptydir

您将获得一个名为testcase的目录,并在此目录中有10个文件和一个目录

emptydir  file.1.txt  file.10.txt file.2.txt  file.3.txt  file.4.txt  file.5.txt  file.6.txt  file.7.txt  file.8.txt  file.9.txt

每个文件包含1000行随机字符串,某些行仅包含数字

现在运行命令

find testcase -type f -print | xargs grep -H '^[0-9]*$' | cut -d: -f 2 | sort -nr | head -1

并且您将获得每个文件中最大的数字行,例如:42。 (当然,这可以更有效地完成,这仅用于演示)

分解:

find testcase -type f -print将打印每个普通文件,LIST(并仅减少到文件)。输出:

testcase/file.1.txt
testcase/file.10.txt
testcase/file.2.txt
testcase/file.3.txt
testcase/file.4.txt
testcase/file.5.txt
testcase/file.6.txt
testcase/file.7.txt
testcase/file.8.txt
testcase/file.9.txt

作为MAP的xargs grep -H '^[0-9]*$'将为列表中的每个文件运行grep命令。 grep通常用作过滤器,例如:command | grep,但现在(使用xargs)将输入(文件名)更改为(仅包含数字的行)。输出,很多行如:

testcase/file.1.txt:1
testcase/file.1.txt:8
....
testcase/file.9.txt:4
testcase/file.9.txt:5

线条结构:filename colon number,只想要数字,所以调用一个纯粹的过滤器,从每行cut -d: -f2中删除文件名。它输出许多行,如:

1
8
...
4
5

现在减少(获得最大数量),sort -nr按数字和反向顺序(desc)对所有数字进行排序,因此其输出如下:

42
18
9
9
...
0
0

并且head -1打印第一行(最大数字)。

当然,您可以使用bash编程构造(循环,条件等)直接编写自己的list / filter / map / reduce函数,或者您可以使用任何完整的脚本语言,如perlawksed“language”等特殊语言,或dc(rpn)等。

有一个特殊的过滤命令,如:

list | filter_command cut -d: -f 2

很简单不需要,因为你可以直接使用

list | cut

1
投票

你可以让awkfilterreduce功能。

过滤:

awk 'NR % 2 { $0 = $0 " [EVEN]" } 1'

降低:

awk '{ p = p + $0 } END { print p }'

0
投票

我完全理解你的问题是一个长期的功能程序员,这里是答案:Bash / unix命令流水线并不像你希望的那样干净。

在上面的例子中:

find mydir -type f -print | xargs grep -H '^[0-9]*$' | cut -d: -f 2 | sort -nr | head  -1
^------list+filter------^   ^--------map-----------^   ^--filter--^   ^compare^  ^reduce^

一个更纯粹的形式看起来像:

find mydir | xargs -L 1 bash -c 'test -f $1 && echo $1' _ | grep -H '^[0-9]*$' | cut -d: -f 2 | sort -nr | head -1
^---list--^^-------filter---------------------------------^^------map----------^^--map-------^  ^reduce^

但是,例如,grep也有一个过滤功能:grep -q mypattern,如果匹配模式,它只返回0。

为了获得更像你想要的东西,你只需要定义一个过滤器bash函数并确保导出它以便它与xargs兼容

但后来你遇到了一些问题。比如,test有二元和一元运算符。您的过滤器功能将如何处理?手,你会决定在这些情况下输出什么?不是不可克服的,但很奇怪。假设只有一元操作:

filter(){
    while read -r LINE || [[ -n "${LINE}" ]]; do
        eval "[[ ${LINE} $1 ]]" 2> /dev/null && echo "$LINE"
    done
}

所以你可以做点什么

seq 1 10 | filter "> 4"
5
6
7
8
9

正如我写的那样,我有点喜欢它

© www.soinside.com 2019 - 2024. All rights reserved.