In Windows, le estensioni dei file sono di solito abbastanza buono:
# 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
Ma, naturalmente, estensioni di file non sono perfetti. A nessuno piace scrivere lunghe liste e molti file vengono comunque chiamati in causa.
Non credo che Unix abbia degli speciali indicatori di testo binario vs nel filesystem. (Beh, il VMS ha funzionato, ma dubito che sia la fonte delle tue abitudini di grep.) Ho esaminato l'implementazione di Grep-I, e apparentemente è solo un'euristica rapida-n-sporca basata sul primo blocco del file. Risulta che è una strategia che ho a bit of experience con. Quindi ecco il mio consiglio su come scegliere una funzione euristica appropriata per i file di testo di Windows:
- Esaminare almeno 1 KB del file. Numerosi formati di file iniziano con un'intestazione simile al testo, ma in seguito eliminerà il parser. Il modo in cui funziona l'hardware moderno, leggendo 50 byte ha approssimativamente lo stesso sovraccarico I/O della lettura di 4KB.
- Se ti interessa direttamente ASCII, esci non appena vedi qualcosa al di fuori dell'intervallo di caratteri [31-127 più CR e LF]. Potresti accidentalmente escludere qualche arte ASCII intelligente, ma cercare di separare questi casi dalla spazzatura binaria non è banale.
- Se si desidera gestire il testo Unicode, lasciare che le librerie MS gestiscano il lavoro sporco. È più difficile di quanto pensi. Da PowerShell è possibile accedere facilmente al metodo statico IMultiLang2 interface (COM) o Encoding.GetEncoding (.NET). Certo, stanno ancora solo indovinando.I commenti di Raymond sullo Notepad detection algorithm (e il link all'interno di Michael Kaplan) sono degni di essere esaminati prima di decidere esattamente come combinare lo & corrispondente alle librerie fornite dalla piattaforma.
- Se il risultato è importante, ad esempio un difetto farà qualcosa di peggio che ingombrare la console grep, quindi non abbiate paura di codificare con precisione alcune estensioni di file per motivi di precisione. Ad esempio, i file * .PDF di tanto in tanto hanno diversi KB di testo nella parte anteriore nonostante sia un formato binario, che porta ai famigerati bug collegati sopra. Allo stesso modo, se si dispone di un'estensione di file che probabilmente contiene dati XML o simili a XML, è possibile provare uno schema di rilevamento simile a Visual Studio's HTML editor. (In alcuni casi SourceSafe 2005 prende in prestito questo algoritmo)
- Qualsiasi altra cosa accada, disporre di un piano di backup ragionevole.
A titolo di esempio, ecco il rilevatore ASCII rapida:
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() }
}
}
}
Il modello di utilizzo ho scelto come target è una clausola in cui-oggetto inserito in cantiere tra "dir" e "ss". Ci sono altri modi, a seconda del tuo stile di scripting.
Il miglioramento dell'algoritmo di rilevamento lungo uno dei percorsi suggeriti è lasciato al lettore.
edit: ho iniziato rispondere al tuo commento in un commento di mio, ma faceva troppo lungo ...
Sopra, ho guardato il problema dal POV di whitelist noto buone sequenze. Nell'applicazione che ho mantenuto, la memorizzazione errata di un file binario come testo ha avuto conseguenze molto peggiori rispetto al contrario. Lo stesso vale per gli scenari in cui si sceglie la modalità di trasferimento FTP da utilizzare, o quale tipo di codifica MIME da inviare a un server di posta elettronica, ecc.
In altri scenari, inserire nella lista nera i termini fittizi e consentire a tutto il resto di essere il testo chiamato è una tecnica altrettanto valida. Mentre U + 0000 è un punto di codice valido, è praticamente mai trovato nel testo del mondo reale. Nel frattempo, \ 00 è abbastanza comune nei file binari strutturati (vale a dire, ogni volta che un campo a lunghezza di byte fisso ha bisogno di riempimento), quindi crea una semplice lista nera. VSS 6.0 ha utilizzato questo controllo da solo e ha funzionato correttamente.
A parte: * i file .zip sono un caso in cui il controllo di \ 0 è più rischioso. A differenza della maggior parte dei binari, il loro blocco strutturato "header" (footer?) È alla fine, non all'inizio. Supponendo una compressione entropica ideale, la probabilità di nessun \ 0 nel primo 1 KB è (1-1/256)^1024 o circa il 2%. Fortunatamente, la semplice scansione del resto del read NTFS del cluster 4KB porterà il rischio fino allo 0,00001% senza dover modificare l'algoritmo o scrivere un altro caso speciale.
Per escludere UTF-8 non valido, aggiungere \ C0-C1 e \ F8-FD e \ FE-FF (dopo aver cercato oltre il possibile BOM) alla lista nera. Molto incompleto dal momento che non stai effettivamente convalidando le sequenze, ma abbastanza vicino per i tuoi scopi. Se vuoi essere più appassionato di questo, è il momento di chiamare una delle librerie della piattaforma come IMULTLang2 :: DetectInputCodepage.
Non sono sicuro del motivo per cui \ C8 (200 decimale) si trova nell'elenco di Grep. Non è una codifica troppo lunga. Ad esempio, la sequenza \ C8 \ 80 rappresenta Ȁ (U + 0200). Forse qualcosa di specifico per Unix.
Non uno script PS, ma 'equivalente findstr' è' findstr/p' che uso nelle console PowerShell in questo modo: 'doskey fs = findstr/spin/a: 4A $ *' quindi utilizzare come 'fs' –
orad