0

In PowerShell, I could run something like this in a one-liner

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

like so

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

The specific functionality of this that I appreciate is being able to reference the pipeline object via $_ to be used in the next command and being able to do conditional execution based on split pieces of output via | foreach {}.

I know in bash it'd be easy to grep multiple patterns at once via

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

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

Is it possible to extend on this in a one liner with what's available by default where I'd be able to do the equivalent of

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

or does Bash only redirect stdout of one command to the stdin of another? I find a lot of misc uses for this dynamic in PowerShell but have not heard of an equivalent in Bash.

This isn't anything blocking and writing a script in the usual syntax works fine, I was just curious because it's incredibly helpful in PS.

Looking for: $_ , | foreach {} equivalents where I could split the output, check conditions on it, then be able to reference the split piece of output via some name. In PSs case, $_

I do know there are many ways PS is fundamentally different from Bash so I would not be surprised if this was specific to PS.

For better context, here's an example in PS where you'd search recursively for files with python and pdf extensions and make a log of them in their respective text files

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

Yes (almost) and no. Bash does not support pipelining 'objects' in the first place – its pipelines, even internal ones, are still built on subprocesses and ordinary stdio (which is a bytestream), and so you can only interact with ordinary lines of text (e.g. a line-by-line list of file paths), not complex objects.

So the closest equivalent would be while read -r <var>; do ...; done. More on that later.


Your specific task can be best handled using a simple for <var> in <words> loop with wildcards:

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

You don't even need to reproduce the explicit checks – you can have two loops instead:

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

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

To do the same recursively, you can a) use Bash's recursive wildcards:

shopt -s globstar

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

or b) use find and loop over each line in stdin:

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

(Yes, I should have used IFS="" read -r <var> to handle filenames which happen to end with a space – but fortunately I don't have any of those on my system so I won't bother.)

Once again, you can skip the manual filename checks and request what you need up-front.

shopt -s globstar

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

Variant b:

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

The same but making use of find's -exec option:

find . -type f -name "*.pdf" -exec do_something_with {} \;
  • This was an excellent, informative response and I'm always grateful I can turn to the SO community for well nuanced responses. Thank you for clearing this up! – saniboy Sep 21 '18 at 6:38

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.