Comando diviso per DOS / Windows tramite Groovy

Uno dei comandi che mi mancano di più da Linux quando lavoro in ambienti Windows / DOS è il comando split. Questo comando estremamente pratico consente di dividere un file di grandi dimensioni in più file più piccoli determinati dalla specifica del numero di righe o del numero di byte (o kilobyte o megabyte) desiderati per i file più piccoli. Esistono molti usi per tale funzionalità, incluso l'adattamento di file su determinati supporti, la creazione di file "leggibili" da applicazioni con limitazioni di lunghezza dei file e così via. Sfortunatamente, non sono a conoscenza di un equivalente diviso per Windows o DOS. È possibile creare script per PowerShell per eseguire operazioni simili, ma l'implementazione è specifica per PowerShell. Sono disponibili anche prodotti di terze parti che eseguono funzionalità simili. Però,queste soluzioni esistenti lasciano a desiderare quanto basta per avere la motivazione per implementare uno split equivalente in Groovy e questo è l'argomento di questo post. Poiché Groovy viene eseguito sulla JVM, questa implementazione potrebbe essere teoricamente eseguita su qualsiasi sistema operativo con un'implementazione moderna di Java Virtual Machine.

Per testare e dimostrare lo split script basato su Groovy, è necessario un tipo di file sorgente. Userò Groovy per generare facilmente questo file sorgente. Il seguente semplice script Groovy, buildFileToSplit.groovy, crea un semplice file di testo che può essere diviso.

#!/usr/bin/env groovy // // buildFileToSplit.groovy // // Accepts single argument for number of lines to be written to generated file. // If no number of lines is specified, uses default of 100,000 lines. // if (!args) { println "\n\nUsage: buildFileToSplit.groovy fileName lineCount\n" println "where fileName is name of file to be generated and lineCount is the" println "number of lines to be placed in the generated file." System.exit(-1) } fileName = args[0] numberOfLines = args.length > 1 ? args[1] as Integer : 100000 file = new File(fileName) // erases output file if it already existed file.delete() 1.upto(numberOfLines, {file << "This is line #${it}.\n"}) 

Questo semplice script utilizza l'handle "args" implicitamente disponibile di Groovy per accedere agli argomenti della riga di comando per lo script buildFileToSplit.groovy. Quindi crea un singolo file di dimensioni basato sull'argomento del numero di righe fornito. Ogni riga è in gran parte non originale e indica "Questa è la riga #" seguita dal numero di riga. Non è un file sorgente di fantasia, ma funziona per l'esempio di divisione. L'istantanea della schermata successiva mostra l'esecuzione e il suo output.

Il file source.txt generato ha questo aspetto (qui viene mostrato solo l'inizio e la fine):

This is line #1. This is line #2. This is line #3. This is line #4. This is line #5. This is line #6. This is line #7. This is line #8. This is line #9. This is line #10. . . . This is line #239. This is line #240. This is line #241. This is line #242. This is line #243. This is line #244. This is line #245. This is line #246. This is line #247. This is line #248. This is line #249. This is line #250. 

Ora è disponibile un file sorgente da dividere. Questo script è notevolmente più lungo perché ho fatto in modo che controlli più condizioni di errore, perché deve gestire più parametri della riga di comando e semplicemente perché fa di più dello script che ha generato il file sorgente. Lo script, chiamato semplicemente split.groovy, è mostrato di seguito:

#!/usr/bin/env groovy // // split.groovy // // Split single file into multiple files similarly to how Unix/Linux split // command works. This version of the script is intended for text files only. // // This script does differ from the Linux/Unix variant in certain ways. For // example, this script's output messages differ in several cases and this // script requires that the name of the file being split is provided as a // command-line argument rather than providing the option to provide it as // standard input. This script also provides a "-v" ("--version") option not // advertised for the Linux/Unix version. // // CAUTION: This script is intended only as an illustration of using Groovy to // emulate the Unix/Linux script command. It is not intended for production // use as-is. This script is designed to make back-up copies of files generated // from the splitting of a single source file, but only one back-up version is // created and is overridden by any further requests. // // //marxsoftware.blogspot.com/ // import java.text.NumberFormat NEW_LINE = System.getProperty("line.separator") // // Use Groovy's CliBuilder for command-line argument processing // def cli = new CliBuilder(usage: 'split [OPTION] [INPUT [PREFIX]]') cli.with { h(longOpt: 'help', 'Usage Information') a(longOpt: 'suffix-length', type: Number, 'Use suffixes of length N (default is 2)', args: 1) b(longOpt: 'bytes', type: Number, 'Size of each output file in bytes', args: 1) l(longOpt: 'lines', type: Number, 'Number of lines per output file', args: 1) t(longOpt: 'verbose', 'Print diagnostic to standard error just before each output file is opened', args: 0) v(longOpt: 'version', 'Output version and exit', args: 0) } def opt = cli.parse(args) if (!opt || opt.h) {cli.usage(); return} if (opt.v) {println "Version 0.1 (July 2010)"; return} if (!opt.b && !opt.l) { println "Specify length of split files with either number of bytes or number of lines" cli.usage() return } if (opt.a && !opt.a.isNumber()) {println "Suffix length must be a number"; cli.usage(); return} if (opt.b && !opt.b.isNumber()) {println "Files size in bytes must be a number"; cli.usage(); return} if (opt.l && !opt.l.isNumber()) {println "Lines number must be a number"; cli.usage(); return} // // Determine whether split files will be sized by number of lines or number of bytes // private enum LINES_OR_BYTES_ENUM { BYTES, LINES } bytesOrLines = LINES_OR_BYTES_ENUM.LINES def suffixLength = opt.a ? opt.a.toBigInteger() : 2 if (suffixLength  1 ? opt.arguments()[1] : "x" try { file = new File(filename) if (!file.exists()) { println "Source file ${filename} is not a valid source file." System.exit(-4) } int fileCounter = 1 firstFileName = "${prefix}${fileSuffixFormat.format(0)}" if (verboseMode) { System.err.println "Creating file ${firstFileName}..." } outFile = createFile(firstFileName) if (bytesOrLines == LINES_OR_BYTES_ENUM.BYTES) { int byteCounter = 0 file.eachByte { if (byteCounter < numberBytes) { outFile << new String(it) } else { nextOutputFileName = "${prefix}${fileSuffixFormat.format(fileCounter)}" if (verboseMode) { System.err.println "Creating file ${nextOutputFileName}..." } outFile = createFile(nextOutputFileName) outFile << new String(it) fileCounter++ byteCounter = 0 } byteCounter++ } } else { int lineCounter = 0 file.eachLine { if (lineCounter < numberLines) { outFile << it << NEW_LINE } else { nextOutputFileName = "${prefix}${fileSuffixFormat.format(fileCounter)}" if (verboseMode) { System.err.println "Creating file ${nextOutputFileName}..." } outFile = createFile(nextOutputFileName) outFile << it << NEW_LINE fileCounter++ lineCounter = 0 } lineCounter++ } } } catch (FileNotFoundException fnfEx) { println System.properties println "${fileName} is not a valid source file: ${fnfEx.toString()}" System.exit(-3) } catch (NullPointerException npe) { println "NullPointerException encountered: ${npe.toString()}" System.exit(-4) } /** * Create a file with the provided file name. * * @param fileName Name of file to be created. * @return File created with the provided name; null if provided name is null or * empty. */ def File createFile(String fileName) { if (!fileName) { println "Cannot create a file from a null or empty filename." return null } outFile = new File(fileName) if (outFile.exists()) { outFile.renameTo(new File(fileName + ".bak")) outFile = new File(fileName) } return outFile } 

Questo script potrebbe essere ottimizzato e meglio modulare, ma soddisfa il suo scopo di dimostrare come Groovy fornisce un buon approccio per l'implementazione di script di utilità indipendenti dalla piattaforma.

L'istantanea della schermata successiva mostra l'uso da parte dello script del supporto CLI integrato di Groovy.

Le successive due istantanee dello schermo dimostrano la divisione del file di origine in file più piccoli rispettivamente per numeri di riga e byte (e utilizzando diverse opzioni di suffisso e nome file). La prima immagine mostra che tre file di output vengono generati quando vengono divisi in 100 righe (250 righe nel file sorgente). L'opzione -a specifica che quattro numeri interi saranno nel nome del file. A differenza della divisione Linux, questo script non garantisce che il numero di interi fornito dall'utente sia sufficiente a coprire il numero di file di output necessari.

La seconda immagine (immagine successiva) mostra lo script che divide il file sorgente in base al numero di byte e utilizza un nome file diverso e solo due numeri interi per la numerazione.

Come accennato in precedenza, questo script è un "taglio approssimativo". Potrebbe essere migliorato in termini di codice stesso e in termini di funzionalità (esteso per supportare meglio i formati binari e per assicurarsi che i suffissi dei nomi dei file siano sufficientemente lunghi per il numero di file di output). Tuttavia, lo script qui dimostra uno dei miei usi preferiti di Groovy: scrivere script indipendenti dalla piattaforma utilizzando le familiari librerie Java e Groovy (SDK e GDK).

Questa storia, "Comando diviso per DOS / Windows tramite Groovy" è stata originariamente pubblicata da JavaWorld.