是否有与 PowerShell 管道对象等效的 bash?

是否有与 PowerShell 管道对象等效的 bash?

在 PowerShell 中,我可以在一行中运行如下命令

Get-ChildItem | foreach {
  if ($_ -match '.+?\.py$' -eq $true) {
    $_ | Do-Thing -WithFile
  }
  if ($_ -match '.+?\.pdf$' -eq $true) {
    $_ | Do-Thing -WithOtherFile
  }
}

就像这样

Get-ChildItem | foreach { if ($_ -match '.+?\.py$' -eq $true) { $_ | Do-Thing -WithFile } if ($_ -match '.+?\.pdf$' -eq $true) { $_ | Do-Thing -WithOtherFile } }

我欣赏的这个具体功能是能够通过引用管道对象以便$_在下一个命令中使用,并且能够通过基于输出的分割片段进行条件执行| foreach {}

我知道在 bash 中,通过以下方式可以很容易地同时 grep 多个模式

> ls | egrep '\.py$|\.pdf$'

some_foo.py
some_bar.py
foo.pdf
bar.pdf

是否可以使用默认情况下可用的方法在一行中扩展此方法,以便我可以执行相当于

| foreach { if ($_ -match '.+?\.py$') {Do-Thing -With $_ } if (...) { Do-Thing -WithOther $_ } }

或者 Bash 是否仅将一个命令的标准输出重定向到另一个命令的标准输入?我发现 PowerShell 中有很多这种动态的杂项用途,但没有听说过 Bash 中有等效用途。

这不是什么阻塞,用通常的语法编写脚本就可以了,我只是好奇,因为它在 PS 中非常有用。

寻找:$_ , | foreach {}等价物,我可以在其中拆分输出、检查其条件,然后能够通过某个名称引用拆分的输出部分。在 PSs 的情况下,$_

我确实知道 PS 在很多方面与 Bash 有着根本的不同,所以如果这是 PS 独有的,我不会感到惊讶。

为了更好地理解上下文,下面是 PS 中的一个例子,你可以递归搜索带有 python 和 pdf 扩展名的文件,并在各自的文本文件中记录它们

Get-ChildItem -Recurse | foreach {
  if ($_ -match '.+?\.py$' -eq $true) {
    $_ | Out-File -encoding utf8 -Append py_list.txt
  }
  if ($_ -match '.+?\.pdf$' -eq $true) {
    $_ | Out-File -encoding utf8 -Append pdf_list.txt
  }
}

答案1

是(几乎)也不是。Bash 首先不支持流水线“对象”——它的管道,即使是内部管道,仍然建立在子进程和普通的 stdio(字节流)上,因此你只能与普通的文本(例如,逐行列出文件路径),而不是复杂的对象。

因此最接近的等价物是while read -r <var>; do ...; done。稍后会详细介绍。


你的具体的任务可以最好的使用简单的for <var> in <words>循环处理通配符

for file in *; do
    if [[ $file == *.py ]]; then
        do_something_with "$file"
    elif [[ $file == *.pdf ]]; then
        do_something_else --with "$file"
    fi
done

您甚至不需要重现明确的检查——您可以改为使用两个循环:

for file in *.py; do
    do_something_with "$file"
done

for file in *.pdf; do
    do_something_else --with "$file"
done

做同样的事情递归地,你可以 a) 使用 Bash 的递归通配符:

shopt -s globstar

for file in **/*; do
    if ...; then ...; fi
done

或者 b) 使用find并循环标准输入中的每一行

find . -type f | while read -r file; do
    if [[ $file == *.py ]]; then
        ...
    fi
done

(是的,我应该习惯IFS="" read -r <var>处理以空格结尾的文件名 - 但幸运的是我的系统上没有这样的文件名,所以我不会打扰。)

再次,您可以跳过手动文件名检查并预先请求您需要的内容。

shopt -s globstar

for file in **/*.pdf; do
    do_something_with "$file"
done

变体b:

find . -type f -name "*.pdf" | while read -r file; do
    do_something_with "$file"
done

相同,但利用寻找的 -exec 选项:

find . -type f -name "*.pdf" -exec do_something_with {} \;

相关内容