Existe um equivalente do bash para um objeto de pipeline do PowerShell?

0

No PowerShell, eu poderia executar algo assim em um one-liner

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

como assim

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

A funcionalidade específica disso que eu aprecio é poder referenciar o objeto de pipeline via $_ para ser usado no próximo comando e ser capaz de fazer uma execução condicional baseada em partes divididas de saída via | foreach {} .

Eu sei que no bash seria fácil usar múltiplos padrões de uma só vez via

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

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

É possível estender isso em um único liner com o que está disponível por padrão, onde eu poderia fazer o equivalente a

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

ou o Bash apenas redireciona o stdout de um comando para o stdin de outro? Eu acho um monte de usos variados para esta dinâmica no PowerShell, mas não ouvi falar de um equivalente no Bash.

Isso não é nada de bloqueio e escrever um script na sintaxe normal funciona bem, eu estava apenas curioso porque é incrivelmente útil no PS.

Procurando por: $_ , | foreach {} equivalents em que eu poderia dividir a saída, verificar as condições, poder referenciar a parte dividida da saída por meio de algum nome. No caso de PSs, $ _

Eu sei que existem muitas maneiras em que o PS é fundamentalmente diferente do Bash, então eu não ficaria surpreso se isso fosse específico para o PS.

Para um melhor contexto, aqui está um exemplo em PS onde você pesquisaria recursivamente arquivos com extensões python e pdf e faria um registro deles em seus respectivos arquivos de texto

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
  }
}
    
por saniboy 21.09.2018 / 06:39

1 resposta

1

Sim (quase) e não. Bash não suporta pipelining 'objects' em primeiro lugar - seus pipelines, mesmo os internos, ainda são construídos em subprocessos e stdio comum (que é um bytestream), e então você só pode interagir com linhas comuns de text (por exemplo, uma lista linha por linha de caminhos de arquivos), não objetos complexos.

Assim, o equivalente mais próximo seria while read -r <var>; do ...; done . Mais sobre isso depois.

Sua tarefa específica pode ser melhor tratada usando um simples loop for <var> in <words> com curingas :

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

Você nem precisa reproduzir as verificações explícitas - você pode ter dois loops:

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

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

Para fazer o mesmo recursivamente , você pode a) usar curingas recursivas do Bash:

shopt -s globstar

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

ou b) use find e faça o loop de cada linha em stdin :

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

(Sim, eu deveria ter usado IFS="" read -r <var> para lidar com nomes de arquivos que acabam com um espaço - mas, felizmente, não tenho nenhum deles no meu sistema, então não vou me incomodar.)

Mais uma vez, você pode pular as verificações manuais de nome de arquivo e solicitar o que precisa antes.

shopt -s globstar

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

Variante b:

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

O mesmo, mas fazendo uso da opção -exec do find :

find . -type f -name "*.pdf" -exec do_something_with {} \;
    
por 21.09.2018 / 08:21