Executando o análogo do PowerShell de *nix grep

Executando o análogo do PowerShell de *nix grep

Existe uma maneira de otimizar o código do PowerShell abaixo (ele agrupa linhas específicas por string contidas em vários arquivos de texto em um único):

$ErrorActionPreference = "Continue"
Start-Transcript -path D:\0xAC1CC07A.log -append
$OutFile = "D:\0xAC1CC07A.txt"
echo "filtering 0xAC1CC07A"
ForEach ($filenm in ((get-childitem -Path D:\FILES\* -include ubuntlive1mb_?????_201509*.txt -recurse -force))) 
{
 $filenm.fullName;
 (Get-Content $filenm) | select-string "0xAC1CC07A" | Add-Content $OutFile
}
Stop-Transcript

Ele funciona bem em pequenas cargas de trabalho, mas em arquivos de texto de 160 mil (mais de 200 GB no total) funciona por mais de 4 dias na minha VM Win2008R2. Surpreendentemente, o Ubuntu 14.04 em hardware virtual semelhante fez o trabalho em 4 horas:

grep --no-filename "0xac1cc07a" ./FILES/ubuntlive1mb_?????_201509*.txt >>./0xAC1CC07A.txt

Ou mais precisamente:

find ./FILES -name "ubuntlive1mb_?????_201509*.txt" -type f -print0 | xargs -0 grep --no-filename "0xac1cc07a" $1 >>./0xAC1CC07A.txt

Não sou bom em PowerShell nem em *nix, todos os scripts acima foram criados pesquisando no Google e copiando e colando.

A caixa do Windows foi otimizada para o sistema de arquivos, desativando nomes de arquivos e atualização de diretório na lista. O Ubuntu acabou de ser instalado imediatamente.

Responder1

Este script Powershell muito simples deve fazer o que você está procurando:

$OutFile = "D:\0xAC1CC07A.txt"
Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt -Recurse | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

Isso apenas adicionará as linhas correspondentes ao arquivo de texto $OutFile. Você também pode usar isso para obter os nomes dos arquivos ou os números das linhas correspondentes, usando as propriedades Filename, Path e LineNumber, em vez de apenas a propriedade Line.

Se você quiser testar um script que será executado em muitos arquivos, mas não quiser esperar que ele termine de verificar todos eles, poderá usar o cmdlet Select-Object para limitar o número de arquivos que ele verificará.

Exemplo:

Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt | Select-Object -First 100 | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

Isso executará o script acima apenas nos primeiros 100 arquivos de texto retornados de Get-ChildItem.

Responder2

Você terá uma saída um pouco diferente (mas isso pode ser resolvido caso haja necessidade), mas pelo que vi é um pouco mais rápido apenas indo para o Select-String diretamente no arquivo em vez de obter o conteúdo do arquivo primeiro.

Select-String "0xAC1CC07A" -Path $filenm.FullName | Add-Content $OutFile

Apenas lembre-se de verificar a saída antes de anexá-la ao arquivo para obtê-la da maneira que deseja.

Quanto à velocidade; Get-ChildItem é notoriamente lento no PowerShell (já que o PowerShell gosta de buscar objetos em vez de apenas representação de texto de objetos) e existem várias soluções alternativas para isso.

A linha Get-ChildItem em seu código pode ser otimizada. Pelo que vi, usar o Filtro é aproximadamente 3,5 vezes mais rápido do que usar inclusões/exclusões em um HDD normal de 7,2k para consumo.

Get-ChildItem -Path "D:\FILES" -Filter "ubuntlive1mb_?????_2015090101*.txt" -Recurse -Force

Se não me falha a memória, versões anteriores do PowerShell tinham alguns problemas com filtro, como se você quisesse todos os arquivos com extensão .htm ele também pegaria os arquivos com extensão .html (como se você tivesse filtrado *.htm*e não *.htm), então você pode querer ficar de olho nisso.

informação relacionada