В 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 было бы легко получить несколько шаблонов одновременно через

> 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 {} эквиваленты, где я мог бы разделить вывод, проверить условия на нем, а затем иметь возможность ссылаться на разделенный вывод через какое-то имя. В случае PS, $ _

Я знаю, что есть много способов, которыми 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 ответ1

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

Чтобы сделать то же самое рекурсивно, вы можете: а) использовать рекурсивные символы Bash:

shopt -s globstar

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

или б) используйте find и зацикливайтесь на каждой строке в stdin:

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

Вариант б:

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

То же самое, но с использованием параметра find 's -exec:

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .