@@ -34,7 +34,7 @@ const (
3434 exitCodeErrorCreatingReport
3535 exitCodeInvalidOutputMode
3636 exitCodeReportFileCreationFailed
37- exitCodeWritingToReportFileFailed
37+ exitCodeOutputDirectoryIsNotReadable
3838)
3939
4040const version = "1.8.0"
@@ -43,15 +43,15 @@ const version = "1.8.0"
4343var defaultExclusionsStr string
4444
4545var flags struct {
46- isHelp func () bool
47- getOutputMode func () string
48- getExcludedFiles func () set.Set [string ]
49- getMinSize func () int64
50- getParallelism func () int
51- isThorough func () bool
52- useStdout func () bool
53- getVerbose func () bool
54- getVersion func () bool
46+ isHelp func () bool
47+ getOutputMode func () string
48+ getExcludedFiles func () set.Set [string ]
49+ getMinSize func () int64
50+ getParallelism func () int
51+ isThorough func () bool
52+ getOutputFilePath func () string
53+ getVersion func () bool
54+ isQuiet func () bool
5555}
5656
5757func setupExclusionsOpt () {
@@ -144,17 +144,19 @@ func setupOutputModeOpt() {
144144 }
145145}
146146
147- func setupStdoutOpt () {
148- stdoutPtr := flag .Bool ("stdout" , false , "report output to stdout" )
149- flags .useStdout = func () bool {
150- return * stdoutPtr
151- }
152- }
147+ const DefaultFileName = ""
153148
154- func setupVerboseOpt () {
155- verbosePtr := flag .Bool ("verbose" , false , "verbose output" )
156- flags .getVerbose = func () bool {
157- return * verbosePtr
149+ func setupOutputFileOpt () {
150+ outputFilePathPtr := flag .StringP ("outputfile" , "f" , DefaultFileName ,
151+ "output file path (will be created, but directory needs to be writeable)" )
152+ flags .getOutputFilePath = func () string {
153+ outputFilePath := * outputFilePathPtr
154+ outputDir := filepath .Dir (outputFilePath )
155+ if ! utils .IsReadableDirectory (outputDir ) { // Deliberately not checking whether directory is writable
156+ fmte .PrintfErr ("error: output directory '%s' does not exist or is not readable\n " , outputDir )
157+ os .Exit (exitCodeOutputDirectoryIsNotReadable )
158+ }
159+ return outputFilePath
158160 }
159161}
160162
@@ -166,6 +168,14 @@ func setupVersionOpt() {
166168 }
167169}
168170
171+ func setupQuietOpt () {
172+ isQuietPtr := flag .BoolP ("quiet" , "q" , false ,
173+ "quiet mode: no output on stdout/stderr, except for duplicates/errors" )
174+ flags .isQuiet = func () bool {
175+ return * isQuietPtr
176+ }
177+ }
178+
169179func setupUsage () {
170180 flag .Usage = func () {
171181 fmte .PrintfErr ("Run \" go-find-duplicates --help\" for usage\n " )
@@ -220,40 +230,34 @@ For more details: https://github.com/m-manu/go-find-duplicates
220230}
221231
222232func setupFlags () {
233+ setupUsage ()
223234 setupExclusionsOpt ()
224235 setupHelpOpt ()
225236 setupMinSizeOpt ()
226237 setupOutputModeOpt ()
227238 setupParallelismOpt ()
228- setupStdoutOpt ()
229239 setupThoroughOpt ()
230- setupUsage ()
231- setupVerboseOpt ()
232240 setupVersionOpt ()
241+ setupQuietOpt ()
242+ setupOutputFileOpt ()
233243}
234244
235245func generateRunID () string {
236246 return time .Now ().Format ("060102_150405" )
237247}
238248
239- func createReportFileIfApplicable (runID string , outputMode string , useStdout bool ) (string , io.WriteCloser ) {
240- if useStdout {
241- return "" , os .Stdout
242- }
249+ func createReportFileIfApplicable (runID string , outputMode string ) (string , io.WriteCloser ) {
243250 var reportFileName string
244251 switch outputMode {
245- case entity .OutputModeStdOut :
246- return "" , os .Stdout
247252 case entity .OutputModeCsvFile :
248253 reportFileName = fmt .Sprintf ("./duplicates_%s.csv" , runID )
249254 case entity .OutputModeTextFile :
250255 reportFileName = fmt .Sprintf ("./duplicates_%s.txt" , runID )
251256 case entity .OutputModeJSON :
252257 reportFileName = fmt .Sprintf ("./duplicates_%s.json" , runID )
253258 default :
254- panic ("unsupport output mode" )
259+ panic ("unsupported output mode - bug in code " )
255260 }
256-
257261 f , err := os .Create (reportFileName )
258262 if err != nil {
259263 fmte .PrintfErr ("error: couldn't create report file: %+v\n " , err )
@@ -276,14 +280,29 @@ func main() {
276280 os .Exit (exitCodeSuccess )
277281 return
278282 }
279- if ! flags .getVerbose () {
283+
284+ if flags .isQuiet () {
280285 fmte .Off ()
281286 }
282287
283288 directories := readDirectories ()
284289 outputMode := flags .getOutputMode ()
285- reportFileName , reportFile := createReportFileIfApplicable (runID , outputMode , flags .useStdout ())
286- defer reportFile .Close ()
290+ reportFileName := flags .getOutputFilePath ()
291+ var reportFile io.Writer
292+ var fErr error
293+ if outputMode == entity .OutputModeStdOut {
294+ reportFile = os .Stdout
295+ } else { // For other modes
296+ if reportFileName == DefaultFileName {
297+ reportFileName , reportFile = createReportFileIfApplicable (runID , outputMode )
298+ } else {
299+ reportFile , fErr = os .Create (reportFileName )
300+ if fErr != nil {
301+ fmte .PrintfErr ("error: couldn't create report file: %+v\n " , fErr )
302+ os .Exit (exitCodeReportFileCreationFailed )
303+ }
304+ }
305+ }
287306
288307 duplicates , duplicateTotalCount , savingsSize , allFiles , fdErr :=
289308 service .FindDuplicates (directories , flags .getExcludedFiles (), flags .getMinSize (),
@@ -303,11 +322,12 @@ func main() {
303322 fmte .Printf ("Found %d duplicates. A total of %s can be saved by removing them.\n " ,
304323 duplicateTotalCount , bytesutil .BinaryFormat (savingsSize ))
305324
306- err := reportDuplicates (duplicates , outputMode , allFiles , runID , reportFile )
307- if err != nil {
308- fmte .PrintfErr ("error while reporting to file: %+v\n " , err )
309- os .Exit (exitCodeWritingToReportFileFailed )
310- } else if reportFileName != "" {
325+ dErr := reportDuplicates (duplicates , outputMode , allFiles , runID , reportFile )
326+ if dErr != nil {
327+ fmte .PrintfErr ("error while reporting to file: %+v\n " , dErr )
328+ os .Exit (exitCodeErrorCreatingReport )
329+ }
330+ if reportFileName != DefaultFileName {
311331 fmte .Printf ("View duplicates report here: %s\n " , reportFileName )
312332 }
313333}
0 commit comments