added -filemask parameter to allow bulk conversions...
..., some refactoring
... | ... |
@@ -11,11 +11,11 @@ license below), you may download the binary. |
11 | 11 |
|
12 | 12 |
Here are the SHA-256 checksums for the binaries: |
13 | 13 |
|
14 |
- f209b32d6fcc07bb5788789bf38419b80ceace8e37b12fd395adfabce1e861f1 csv2xlsx_386.exe |
|
15 |
- 76be18318b797742985b4547ddb502688b014bb13f19a1b5d9e886869446e33e csv2xlsx_amd64.exe |
|
16 |
- 97fce8d687a73c770dd5463c01ee126bc82fafb1da5fd49dd4751dce06f2cebd csv2xlsx_linux_386 |
|
17 |
- a931741383cb68a10bad6a107b0956d5f9e1e23eecb4c11c399bb463307c77bc csv2xlsx_linux_amd64 |
|
18 |
- 7fbfce2072fedb66fe1a26b1e28399e3ef3b68fc3179778f1c59230fecbda904 csv2xlsx_osx |
|
14 |
+ 9cd013ac244c0d364302da1eab724ea93831f6088188bcaaae08545f00a4582a csv2xlsx_386.exe |
|
15 |
+ 535c3937447497bd0769296d434c809b41e1ead7bc463f560888ec51cfb95794 csv2xlsx_amd64.exe |
|
16 |
+ d491329c16f44d82c6d50bb4afa74bd704f25ade7e93b28d0ec0b747648c9a6e csv2xlsx_linux_386 |
|
17 |
+ cfa63a9eb6ab4d9c418afe95cf54524b32611a01d970e95ed57831b1368406f1 csv2xlsx_linux_amd64 |
|
18 |
+ daa9ff8f2ccb49b6ccf4fd7d7b600011a8bd4ba9fe1b3870ce15dcbbe6fa1092 csv2xlsx_osx |
|
19 | 19 |
|
20 | 20 |
### Usage |
21 | 21 |
|
... | ... |
@@ -41,13 +41,18 @@ Please see below for a list of command line options. |
41 | 41 |
encoding string to use for the CSV file, case-insensitive (defaults to "utf-8") |
42 | 42 |
-exceldateformat string |
43 | 43 |
Excel format for date cells (default as in Excel) |
44 |
- -h display usage information |
|
44 |
+ -filemask |
|
45 |
+ bulk mode, specify a file mask here (e.g. "/use/docs/datalib/2018*.csv") |
|
46 |
+ make sure to quote the filespace to prevent shell globbing |
|
47 |
+ -h |
|
45 | 48 |
-help |
46 | 49 |
display usage information |
47 | 50 |
-infile string |
48 | 51 |
full pathname of input file (CSV file) |
49 | 52 |
-outfile string |
50 | 53 |
full pathname of output file (.xlsx file) |
54 |
+ -outdir |
|
55 |
+ path to a target directory for the xlsx files (must exist and be writable) |
|
51 | 56 |
-rows string |
52 | 57 |
list of line numbers to use (1,2,8 or 1,3-14,28) |
53 | 58 |
-sheet string |
... | ... |
@@ -143,9 +148,12 @@ I am still amazed what you can accomplish within less than 200 lines of code in |
143 | 148 |
2017-12-21 0.2 |
144 | 149 |
Added option --encoding |
145 | 150 |
|
146 |
- 2017-06-20 0.3 |
|
151 |
+ 2018-06-20 0.3 |
|
147 | 152 |
Added better version of ParseFloat to allow scientific number notation |
148 | 153 |
|
154 |
+ 2018-06-21 0.3.1 |
|
155 |
+ Added -filemask option to allow bulk processing |
|
156 |
+ |
|
149 | 157 |
|
150 | 158 |
### License |
151 | 159 |
|
... | ... |
@@ -24,6 +24,8 @@ var ( |
24 | 24 |
parmSheet string |
25 | 25 |
parmInFile string |
26 | 26 |
parmOutFile string |
27 |
+ parmOutDir string |
|
28 |
+ parmFileMask string |
|
27 | 29 |
parmEncoding string |
28 | 30 |
parmColSep rune |
29 | 31 |
parmDateFormat string |
... | ... |
@@ -133,6 +135,8 @@ func parseRangeString(rangeStr string) map[int]string { |
133 | 135 |
func parseCommandLine() { |
134 | 136 |
flag.StringVar(&parmInFile, "infile", "", "full pathname of input file (CSV file)") |
135 | 137 |
flag.StringVar(&parmOutFile, "outfile", "", "full pathname of output file (.xlsx file)") |
138 |
+ flag.StringVar(&parmFileMask, "filemask", "", "file mask for bulk processing (overwrites -infile/-outfile)") |
|
139 |
+ flag.StringVar(&parmOutDir, "outdir", "", "target directory for the .xlsx file (not to be used with outfile)") |
|
136 | 140 |
flag.StringVar(&parmDateFormat, "dateformat", "2006-01-02", "format for CSV date cells (default YYYY-MM-DD)") |
137 | 141 |
flag.StringVar(&parmExcelDateFormat, "exceldateformat", "", "Excel format for date cells (default as in Excel)") |
138 | 142 |
flag.StringVar(&parmCols, "columns", "", "column range to use (see below)") |
... | ... |
@@ -175,10 +179,18 @@ func parseCommandLine() { |
175 | 179 |
`) |
176 | 180 |
os.Exit(1) |
177 | 181 |
} |
178 |
- if _, err := os.Stat(parmInFile); os.IsNotExist(err) { |
|
179 |
- fmt.Println("Input file does not exist, exiting.") |
|
182 |
+ |
|
183 |
+ if parmOutFile != "" && parmOutDir != "" { |
|
184 |
+ fmt.Println("Cannot use -outfile and -outdir together (-outdir to be used with -filemask), exiting.") |
|
180 | 185 |
os.Exit(1) |
181 | 186 |
} |
187 |
+ |
|
188 |
+ if parmFileMask == "" { |
|
189 |
+ if _, err := os.Stat(parmInFile); os.IsNotExist(err) { |
|
190 |
+ fmt.Println("Input file does not exist, exiting.") |
|
191 |
+ os.Exit(1) |
|
192 |
+ } |
|
193 |
+ } |
|
182 | 194 |
} |
183 | 195 |
|
184 | 196 |
// loadInputFile reads the complete input file into a matrix of strings. |
... | ... |
@@ -407,11 +419,36 @@ func processDataColumns(excelRow *xlsx.Row, rownum int, csvLine []string) { |
407 | 419 |
} |
408 | 420 |
} |
409 | 421 |
|
410 |
-// the main entry function |
|
411 |
-func main() { |
|
412 |
- // preflight stuff |
|
413 |
- parseCommandLine() |
|
414 |
- rows := loadInputFile(parmInFile) |
|
422 |
+// getInputFiles retrieves a list of input files for a given filespec |
|
423 |
+// returns a slice of strings or aborts the program on error |
|
424 |
+func getInputFiles(inFileSpec string) []string { |
|
425 |
+ files, err := filepath.Glob(inFileSpec) |
|
426 |
+ if err != nil { |
|
427 |
+ fmt.Println(err) |
|
428 |
+ os.Exit(1) |
|
429 |
+ } |
|
430 |
+ return files |
|
431 |
+} |
|
432 |
+ |
|
433 |
+// buildOutputName generates the .xlsx file name for a given input file |
|
434 |
+// if the user specified the -outdir option, use this directory as target path |
|
435 |
+// return a string with the target file name |
|
436 |
+func buildOutputName(infile string) string { |
|
437 |
+ outfile := strings.TrimSuffix(infile, filepath.Ext(infile)) + ".xlsx" |
|
438 |
+ if parmOutDir != "" { |
|
439 |
+ if _, err := os.Stat(parmOutDir); err == nil { |
|
440 |
+ outfile = filepath.Join(parmOutDir, filepath.Base(outfile)) |
|
441 |
+ } else { |
|
442 |
+ fmt.Println(fmt.Sprintf("Output directory %q does not exist, exiting.", parmOutDir)) |
|
443 |
+ os.Exit(1) |
|
444 |
+ } |
|
445 |
+ } |
|
446 |
+ return outfile |
|
447 |
+} |
|
448 |
+ |
|
449 |
+// convertFile does the conversion from CSV to Excel .xslx |
|
450 |
+func convertFile(infile, outfile string) { |
|
451 |
+ rows := loadInputFile(infile) |
|
415 | 452 |
setRangeInformation(len(rows), len(rows[0])) |
416 | 453 |
|
417 | 454 |
// excel stuff, create file, add worksheet, define a right-aligned style |
... | ... |
@@ -429,5 +466,33 @@ func main() { |
429 | 466 |
processDataColumns(excelRow, rownum, line) |
430 | 467 |
} |
431 | 468 |
} |
432 |
- workBook.Save(parmOutFile) |
|
469 |
+ err := workBook.Save(outfile) |
|
470 |
+ if err != nil { |
|
471 |
+ fmt.Println(err) |
|
472 |
+ os.Exit(1) |
|
473 |
+ } |
|
474 |
+} |
|
475 |
+ |
|
476 |
+// the main entry function |
|
477 |
+func main() { |
|
478 |
+ // preflight stuff |
|
479 |
+ var fileList []string |
|
480 |
+ parseCommandLine() |
|
481 |
+ |
|
482 |
+ // either glob the file mask or retrieve the single input file |
|
483 |
+ // this way we can just iterate over the slice (and maybe later |
|
484 |
+ // add an option for a specified list of files) |
|
485 |
+ if parmFileMask != "" { |
|
486 |
+ fmt.Println("Found -filemask parameter, running in bulk mode") |
|
487 |
+ fileList = getInputFiles(parmFileMask) |
|
488 |
+ } else { |
|
489 |
+ fileList = getInputFiles(parmInFile) |
|
490 |
+ } |
|
491 |
+ |
|
492 |
+ // iterate over list of files to process and convert them |
|
493 |
+ for _, infile := range fileList { |
|
494 |
+ outfile := buildOutputName(infile) |
|
495 |
+ fmt.Println("Converting", infile, "=>", outfile) |
|
496 |
+ convertFile(infile, outfile) |
|
497 |
+ } |
|
433 | 498 |
} |