content - search in powershell
Script de búsqueda de PowerShell que ignora los archivos binarios (2)
En Windows, las extensiones de archivos suelen ser lo suficientemente buenas:
# all C# and related files (projects, source control metadata, etc)
dir -r -fil *.cs* | ss foo
# exclude the binary types most likely to pollute your development workspace
dir -r -exclude *exe, *dll, *pdb | ss foo
# stick the first three lines in your $profile (refining them over time)
$bins = new-list string
$bins.AddRange( [string[]]@("exe", "dll", "pdb", "png", "mdf", "docx") )
function IsBin([System.IO.FileInfo]$item) { !$bins.Contains($item.extension.ToLower()) }
dir -r | ? { !IsBin($_) } | ss foo
Pero, por supuesto, las extensiones de archivo no son perfectas. A nadie le gusta escribir largas listas, y muchos archivos son mal nombrados de todos modos.
No creo que Unix tenga ningún indicador binario especial frente a texto en el sistema de archivos. (Bueno, VMS sí, pero dudo que esa sea la fuente de tus hábitos grep.) Miré la implementación de Grep -I, y aparentemente es solo una heurística rápida y sucia basada en la primera parte del archivo. Resulta que es una estrategia con la que tengo un poco de experiencia . Así que este es mi consejo sobre cómo elegir una función heurística adecuada para los archivos de texto de Windows:
- Examine al menos 1 KB del archivo. Muchos formatos de archivo comienzan con un encabezado que se parece al texto, pero reventará el analizador poco después. La forma en que funciona el hardware moderno, leer 50 bytes tiene aproximadamente la misma sobrecarga de E / S que leer 4 KB.
- Si solo te preocupa el ASCII directo, sal en cuanto veas algo fuera del rango de caracteres [31-127 más CR y LF]. Puede que accidentalmente excluya algún arte ASCII inteligente, pero tratar de separar esos casos de la basura binaria no es trivial.
- Si desea manejar texto Unicode, permita que las bibliotecas MS manejen el trabajo sucio. Es más difícil de lo que piensas Desde Powershell puede acceder fácilmente a la interfaz IMultiLang2 (COM) o al método estático Encoding.GetEncoding (.NET). Por supuesto, todavía están adivinando. Los comentarios de Raymond sobre el algoritmo de detección Notepad (y el enlace dentro de Michael Kaplan) valen la pena revisar antes de decidir exactamente cómo desea mezclar y combinar las bibliotecas proporcionadas por la plataforma.
- Si el resultado es importante, es decir, una falla hará algo peor que saturar su consola grep, entonces no tenga miedo de codificar algunas extensiones de archivo por razones de precisión. Por ejemplo, los archivos * .PDF ocasionalmente tienen varios KB de texto en el frente a pesar de ser un formato binario, lo que lleva a los errores notorios vinculados anteriormente. De forma similar, si tiene una extensión de archivo que probablemente contenga datos similares a XML o XML, puede probar un esquema de detección similar al editor HTML de Visual Studio . (SourceSafe 2005 realmente toma prestado este algoritmo para algunos casos)
- Pase lo que pase, tenga un plan de respaldo razonable.
Como ejemplo, aquí está el detector ASCII rápido:
function IsAscii([System.IO.FileInfo]$item)
{
begin
{
$validList = new-list byte
$validList.AddRange([byte[]] (10,13) )
$validList.AddRange([byte[]] (31..127) )
}
process
{
try
{
$reader = $item.Open([System.IO.FileMode]::Open)
$bytes = new-object byte[] 1024
$numRead = $reader.Read($bytes, 0, $bytes.Count)
for($i=0; $i -lt $numRead; ++$i)
{
if (!$validList.Contains($bytes[$i]))
{ return $false }
}
$true
}
finally
{
if ($reader)
{ $reader.Dispose() }
}
}
}
El patrón de uso al que me refiero es una cláusula where-object insertada en la interconexión entre "dir" y "ss". Hay otras formas, dependiendo de su estilo de scripting.
La mejora del algoritmo de detección a lo largo de una de las rutas sugeridas se deja al lector.
editar: comencé a responder a tu comentario en un comentario mío, pero pasó demasiado tiempo ...
Arriba, miré el problema desde el punto de vista de la lista blanca de secuencias bien conocidas. En la aplicación que mantuve, almacenar incorrectamente un archivo binario como texto tenía peores consecuencias que viceversa. Lo mismo es cierto para los escenarios en los que se elige qué modo de transferencia de FTP utilizar, o qué tipo de codificación MIME enviar a un servidor de correo electrónico, etc.
En otros escenarios, poner en la lista negra lo obviamente falso y permitir que todo lo demás se llame texto es una técnica igualmente válida. Mientras que U + 0000 es un punto de código válido, casi nunca se encuentra en el texto del mundo real. Mientras tanto, / 00 es bastante común en archivos binarios estructurados (es decir, cuando un campo de longitud de byte fijo necesita relleno), por lo que es una gran lista negra simple. VSS 6.0 utilizó este control solo y lo hizo bien.
Además: *. Los archivos .zip son un caso donde la comprobación de / 0 es más arriesgada. A diferencia de la mayoría de los binarios, su bloque estructurado de "encabezado" (pie de página?) Está al final, no al principio. Suponiendo la compresión de entropía ideal, la posibilidad de no / 0 en el primer 1KB es (1-1 / 256) ^ 1024 o aproximadamente 2%. Afortunadamente, simplemente escaneando el resto de la lectura NTFS del clúster de 4KB disminuirá el riesgo hasta 0.00001% sin tener que cambiar el algoritmo o escribir otro caso especial.
Para excluir el UTF-8 no válido, agregue / C0-C1 y / F8-FD y / FE-FF (una vez que haya buscado más allá de la posible lista de materiales) a la lista negra. Muy incompleto ya que en realidad no está validando las secuencias, pero lo suficientemente cerca para sus propósitos. Si desea obtener algo más elegante que este, es hora de llamar a una de las bibliotecas de plataforma como IMultiLang2 :: DetectInputCodepage.
No estoy seguro de por qué / C8 (200 decimal) está en la lista de Grep. No es una codificación demasiado larga. Por ejemplo, la secuencia / C8 / 80 representa Ȁ (U + 0200). Tal vez algo específico de Unix.
Estoy realmente acostumbrado a hacer grep -iIr
en el shell de Unix, pero aún no he podido obtener un equivalente de PowerShell.
Básicamente, el comando anterior busca las carpetas de destino recursivamente e ignora los archivos binarios debido a la opción "-I". Esta opción también es equivalente a la --binary-files=without-match
, que dice "tratar los archivos binarios como no coincidentes con la cadena de búsqueda"
Hasta ahora he estado usando Get-ChildItems -r | Select-String
Get-ChildItems -r | Select-String
como mi reemplazo de grep de PowerShell con los ocasionales Where-Object
agregados. Pero no he encontrado una manera de ignorar todos los archivos binarios como grep -I
comando grep -I
hace.
¿Cómo se pueden filtrar o ignorar los archivos binarios con Powershell?
Entonces, para una ruta determinada, solo quiero que Select-String
busque archivos de texto.
EDITAR: Algunas horas más en Google produjeron esta pregunta. Cómo identificar el contenido de un archivo es ASCII o binario . La pregunta dice "ASCII", pero creo que el autor quiso decir "Texto codificado", como yo.
EDITAR: Parece que es necesario isBinary()
un isBinary()
para resolver este problema. Probablemente una utilidad de línea de comandos C # para hacerlo más útil.
EDITAR: Parece que lo que grep
está haciendo es buscar ASCII NUL Byte o UTF-8 Overlong . Si eso existe, considera que el archivo es binario. Esta es una sola llamada a memchr () .
Ok, después de unas horas más de investigación, creo que encontré mi solución. Sin embargo, no lo marcaré como la respuesta.
Pro Windows Powershell tenía un ejemplo muy similar. Me había olvidado por completo que tenía esta excelente referencia. Por favor cómprelo si está interesado en Powershell. Se entró en detalle en Get-Content y Unicode BOMs.
Esta respuesta a preguntas similares también fue muy útil con la identificación Unicode.
Aquí está el guión. Por favor, avíseme si conoce algún problema que pueda tener.
# The file to be tested
param ($currFile)
# encoding variable
$encoding = ""
# Get the first 1024 bytes from the file
$byteArray = Get-Content -Path $currFile -Encoding Byte -TotalCount 1024
if( ("{0:X}{1:X}{2:X}" -f $byteArray) -eq "EFBBBF" )
{
# Test for UTF-8 BOM
$encoding = "UTF-8"
}
elseif( ("{0:X}{1:X}" -f $byteArray) -eq "FFFE" )
{
# Test for the UTF-16
$encoding = "UTF-16"
}
elseif( ("{0:X}{1:X}" -f $byteArray) -eq "FEFF" )
{
# Test for the UTF-16 Big Endian
$encoding = "UTF-16 BE"
}
elseif( ("{0:X}{1:X}{2:X}{3:X}" -f $byteArray) -eq "FFFE0000" )
{
# Test for the UTF-32
$encoding = "UTF-32"
}
elseif( ("{0:X}{1:X}{2:X}{3:X}" -f $byteArray) -eq "0000FEFF" )
{
# Test for the UTF-32 Big Endian
$encoding = "UTF-32 BE"
}
if($encoding)
{
# File is text encoded
return $false
}
# So now we''re done with Text encodings that commonly have ''0''s
# in their byte steams. ASCII may have the NUL or ''0'' code in
# their streams but that''s rare apparently.
# Both GNU Grep and Diff use variations of this heuristic
if( $byteArray -contains 0 )
{
# Test for binary
return $true
}
# This should be ASCII encoded
$encoding = "ASCII"
return $false
Guarde este script como isBinary.ps1
Este script tiene todos los archivos de texto o binarios que probé correctamente.