任何简单的方法都可以执行循环管道,例如 ` ls | grep ".*.7z" | 7z -e {}`

任何简单的方法都可以执行循环管道,例如 ` ls | grep ".*.7z" | 7z -e {}`

我很好奇,是否有一个函数/命令可以应对循环管道?

很容易出现需求不足的情况

  1. 你运行命令返回多行
  2. 你需要处理每一行
  3. 将每一行传递给另一个函数/命令

例如:

您需要找到一些与模式匹配的路径,然后将其移动到另一个位置:

# 1.
find path_A -name '*AAA*' -exec mv -t path_B {} +

# 2.
find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B 

人们会感到困惑,

  1. 为什么需要 mv -t(我的意思是,通过一个 [for 循环管道] 你可以使用mv {} some_path,就像你没有管道所写的一样)
  2. 为什么 exec 或 | xargs 会这样写,xargs 需要更多的选项,但其实在很多计算机语言中是不需要的。
  3. 为什么我不能使用 ls ... mv -t path_B {} + ,或者ls | xargs -0 -I {} mv {} path_B换句话说,为什么我换到另一个命令后它不起作用?
  4. 为什么我改为mv7z会 失败find path_A -name '*AAA*' -exec 7z -t {} +

注意:以上不是问题,只是例子!!


我问这个问题是为了寻找一种常见且简单的方法来应对循环管道。有没有一种简单而常见的方法可以做到:

# 0
command with option (usually you use a command like this)

# 1
command with option [for loop pipe] command with option | command with option
# 2
command with option [for loop pipe] command with option | command with option [for loop pipe] command with option
# 3 
....

因此,当你使用 [for loop pipe] 时不需要进行任何更改command with option 。这对于日常使用来说会方便很多。

更新

我举出所有的例子只是想说明,当不同的命令有自己的风格来应对多行输入时,正常的方式会让人困惑,它需要execxargs其他一些选项。如果我们有一个for pipe 命令可以使用选项连接两个命令。这样使用起来就方便多了

我的意思是,如果你使用一种高级语言。例如 Python,

def pipe(x, func, *func_options):
    return func(x, *func_options)

def forloop_pipe(x, func, *func_options):   
    return [func(i, *func_options) for i in x.split('\n')]


def ls(*options)
    pass

def mv(*options)
    pass

a = ls('*.txt') 
b = forloop_pipe(a, mv, '/home')

这是一个非常简单的例子,有很多缺陷,但它解释了我在 bash 命令中尝试获得的内容。

答案1

我不确定这是否是对该问题的直接回答(有多个问题),但我认为您问的是类似这样的问题:

  • for循环命令替换或者
  • while循环流程替代

for循环$(command substitution)

命令替换允许命令的输出替换命令本身。我们可以for用这种方式将此功能与循环结合起来:

for item in "$(find . -type f)"
do
    echo "$item" | tee -a ./"file-list.txt"
done

while循环<(process substitution)

工艺替代允许使用文件名引用进程的输入或输出。我们可以while借助内置的 ,将此功能与循环结合起来read,如下所示:

while IFS= read -r item
do
    echo "$item" | tee -a ./"file-list.txt"
done < <(find . -type f)

在最后一部分中< <(find . -type f),第一个<表示 while 的 stdin loop<(find . -type f)将被视为文件。有关构造的更多信息,IFS= read -r line请参阅这篇很棒的文章斯蒂芬·查泽拉斯 (Stéphane Chazelas) 的作品。

您可以使用管道代替来实现与上述相同的效果流程替代(但不建议采用这种方式,因为它可能会导致错误):

find . -type f | while IFS= read -r file
do
    echo "$file" | tee -a ./"file-list.txt"
done

将循环的输出通过管道传输到另一个命令

此外,我们可以将循环的输出通过管道或重定向到另一个命令或函数:

while IFS= read -r -d '' item; do
    # Compose the name of the new file
    DIR="$(dirname "${item}")"
    FILE_NAME="$(basename "${item}")"
    NEW_FILE_NAME="new-${FILE_NAME}"

    # move the file and suppress the potential output 
    # of the 'mv' command by redirecting it to a log file 
    mv "${item}" "${DIR}/${NEW_FILE_NAME}" >>/tmp/mv-loop.log 2>&1

    # output the name of the new file in order to be processed 
    # by the next (piped) command
    printf '%b' "${DIR}/${NEW_FILE_NAME}"'\0'
done < <(find . -type f -print0) | xargs -0 -I{} 7z a -t7z "the_archive_name.7z" {}

请注意,在上面的例子中,到处都使用了空分隔符:

  • find . -print0
  • IFS= read -r -d '' item-参考
  • printf '%b' a'\0'b-参考
  • xargs -0(在这种情况下我们实际上不需要命令中的-I{}and )。{}xargs

此示例将递归移动(重命名)所有文件,但在存档中目录结构不会出现。

答案2

有没有一个简单而常见的方法来做

答案:不,每个命令都是不同的。


关于您的问题:

  1. 您不需要-t,但随后mv将为每个文件运行,这会慢很多。您需要以而不是来结束您的-exec命令。\;+

  2. 管道与循环无关for,管道发送标准输出从管道左侧到标准输入在右侧。xargs读取自标准输入(或来自带有选项的文件-a)并使用从输入中获取的参数运行命令。

  3. ls没有-exec选择。ls 将要可以使用xargs,但不能使用xargs -0becausels没有用于 NULL 分隔输出的选项。这也是为什么ls你根本不应该使用管道(文件名可以有换行符)。

  4. 对于mv-t目标目录,而7z对于档案类型,您为什么认为它应该有效?

在我看来,你使用的命令似乎你并不真正理解。这对任何编程或脚本语言都不起作用。


顺便说一句:您应该使用xargs选项-r来避免空输入的问题。

答案3

也许你的答案是xargs -n 1

看这个例子:仅 1 行(为 gzip 生成循环):

ls | grep -i '.csv'  | xargs -n 1 gzip

或者

ls  | grep -i '.gz'  | xargs -n 1 gunzip

相关内容