循环输出管道可防止局部变量修改


11

我正在尝试编写一个简单的bash函数,该函数将许多文件和/或目录作为其参数。这应该:

  1. 完全限定文件名。
  2. 对它们进行排序。
  3. 删除重复项。
  4. 打印所有实际存在的内容。
  5. 返回不存在的文件数。

我有一个脚本,几乎可以执行我想要的操作,但是无法进行排序。按原样返回脚本的返回值是正确的,但是输出不是(未排序和重复的)。如果我取消注释所示的| sort -u语句,则输出正确,但返回值始终为0

注意:欢迎使用更简单的解决方案来解决问题,但问题实际上是为什么在我的代码中会发生这种情况。也就是说,为什么添加管道似乎停止脚本增加变量的作用r

这是脚本:

function uniqfile
{
    local r=0 

    for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done #| sort -u    ## remove that comment

    return $r
}

只是一个小的观察。您可以减少for arg in "$@"for arg。“如果'用言语...;' 不存在,则假定为“在“ $ @”中”。-帮助
manatwork 2011年

Answers:


15

由于此功能,这是众所周知的bash陷阱:

管道中的每个命令都作为单独的进程(即,在子Shell中)执行。

因此,修改后的变量在子外壳中是局部的,一旦回到父级中就不可见。

为避免这种情况,请重新编写代码以避开管道,并使用流程替换:

 for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done > >(sort -u)

谢谢。那很棒。我想知道您能否告诉我>(..command..)构造的名称。我我知道它是如何工作的,但是我应该做一些进一步的阅读。
tjm 2011年

2
@tjm:这称为流程替换
enzotib 2011年

Bash中的进程替换有多种形式:tldp.org/LDP/abs/html/process-sub.html
slm

进程替换进程间通信的一种形式,它允许命令的输入或输出显示为文件。该命令由命令shell内联替换,通常在此位置通常会出现文件名。这允许通常只接受文件的程序直接从另一个程序读取或写入另一个程序。
2015年

3

| sort -u当前一比特(所以整个for循环)在一个子进程运行力(bash中需要一个“标准输出”重定向到sort“STDIN”(上网本似乎觉得kshbash处理这种情况稍有不同..第一个或最后管道序列中的命令被放入子外壳中?)

该线程解决了类似的问题,并且在结尾处有一个整洁的解决方案:http : //ubuntuforums.org/showthread.php?t=312017

摘抄
    #!/bin/bash
    exec 3< <(du | sort -n)  

    n=0
    while read size dir; do
      [ $size -gt 1000 ] && ((n++))
    done <&3
    exec 3<&-

    echo "Found $n too big files"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.