Perpecahan Perintah untuk DOS / Windows Via Groovy

Salah satu arahan yang paling saya rindukan dari Linux ketika bekerja di persekitaran Windows / DOS adalah perintah split. Perintah yang sangat berguna ini membolehkan seseorang memisahkan fail besar menjadi beberapa fail yang lebih kecil yang ditentukan oleh spesifikasi sama ada dengan bilangan baris atau bilangan bait (atau kilobyte atau megabait) yang diinginkan untuk fail yang lebih kecil. Terdapat banyak kegunaan untuk fungsi tersebut termasuk memasukkan fail ke media tertentu, menjadikan fail "dibaca" oleh aplikasi dengan sekatan panjang fail, dan sebagainya. Malangnya, saya tidak mengetahui perbezaan yang setara untuk Windows atau DOS. PowerShell boleh dibuat skrip untuk melakukan sesuatu seperti ini, tetapi pelaksanaannya khusus untuk PowerShell. Terdapat juga produk pihak ketiga yang mempunyai fungsi yang serupa. Walau bagaimanapun,penyelesaian yang ada ini cukup untuk diinginkan sehingga saya mempunyai motivasi untuk melaksanakan pembahagian yang setara di Groovy dan itulah tajuk posting ini. Kerana Groovy berjalan di JVM, implementasi ini dapat dilakukan secara teoritis pada sistem operasi apa pun dengan implementasi Java Virtual Machine yang moden.

Untuk menguji dan menunjukkan skrip perpecahan berasaskan Groovy, diperlukan beberapa jenis fail sumber. Saya akan menggunakan Groovy untuk menghasilkan fail sumber ini dengan mudah. Skrip Groovy sederhana berikut, buildFileToSplit.groovy, membuat fail teks mudah yang boleh dibahagi.

#!/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"}) 

Skrip mudah ini menggunakan pegangan "args" yang tersedia secara tersirat untuk mengakses argumen baris perintah untuk skrip buildFileToSplit.groovy. Ia kemudian membuat satu fail ukuran berdasarkan argumen bilangan baris yang disediakan. Setiap baris sebahagian besarnya tidak asli dan menyatakan "Ini adalah baris #" diikuti dengan nombor garis. Ini bukan fail sumber mewah, tetapi berfungsi untuk contoh pemisahan. Petikan skrin seterusnya menunjukkan ia dijalankan dan outputnya.

Fail source.txt yang dihasilkan kelihatan seperti ini (hanya permulaan dan penghujungnya ditunjukkan di sini):

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. 

Kini ada fail sumber yang boleh dibahagi. Skrip ini jauh lebih panjang kerana saya telah membuatnya memeriksa lebih banyak keadaan ralat, kerana perlu menangani lebih banyak parameter baris perintah, dan hanya kerana ia lebih banyak daripada skrip yang menghasilkan fail sumber. Skrip, hanya disebut split.groovy, ditunjukkan seterusnya:

#!/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 } 

Skrip ini dapat dioptimumkan dan dimodulasi dengan lebih baik, tetapi memenuhi tujuannya untuk menunjukkan bagaimana Groovy memberikan pendekatan yang bagus untuk menerapkan skrip utiliti bebas platform.

Cuplikan skrin seterusnya menunjukkan penggunaan skrip sokongan CLI terbina dalam Groovy.

Dua tangkapan skrin seterusnya menunjukkan membelah fail sumber menjadi fail yang lebih kecil dengan nombor baris dan byte masing-masing (dan menggunakan akhiran dan pilihan nama fail yang berbeza). Gambar pertama menunjukkan bahawa tiga fail output dihasilkan apabila dibahagikan kepada 100 baris (250 baris dalam fail sumber). Pilihan -a menetapkan bahawa empat tempat bilangan bulat akan berada di nama fail. Tidak seperti perpecahan Linux, skrip ini tidak menjamin bahawa bilangan bulat yang disediakan pengguna cukup untuk menampung jumlah fail output yang diperlukan.

Gambar kedua (gambar seterusnya) menunjukkan skrip membelah fail sumber berdasarkan bilangan bait dan menggunakan nama fail yang berbeza dan hanya dua bilangan bulat untuk penomboran.

Seperti yang disebutkan di atas, skrip ini adalah "potongan kasar." Ia boleh diperbaiki dari segi kod itu sendiri dan juga dari segi fungsi (diperluas untuk menyokong format binari yang lebih baik dan untuk memastikan akhiran nama fail cukup panjang untuk jumlah fail output). Walau bagaimanapun, skrip di sini menunjukkan salah satu kegunaan Groovy kegemaran saya: untuk menulis skrip bebas platform menggunakan perpustakaan Java dan Groovy (SDK dan GDK).

Kisah ini, "Perintah split untuk DOS / Windows Via Groovy" pada mulanya diterbitkan oleh JavaWorld.