diff --git a/cmd/ls-main.go b/cmd/ls-main.go index f6e6d46c8a..aea1918f1c 100644 --- a/cmd/ls-main.go +++ b/cmd/ls-main.go @@ -60,6 +60,10 @@ var ( Name: "zip", Usage: "list files inside zip archive (MinIO servers only)", }, + cli.StringFlag{ + Name: "sort", + Usage: "sort by field. Only possible value for now: size", + }, } ) @@ -177,6 +181,14 @@ func checkListSyntax(cliCtx *cli.Context) ([]string, doListOptions) { timeRef := parseRewindFlag(cliCtx.String("rewind")) + sortBy := cliCtx.String("sort") + switch sortBy { + case "": + case "size": + default: + fatalIf(errInvalidArgument().Trace(args...), "Unsupported sort option '"+sortBy+"'. Only 'size' is supported.") + } + if listZip && (withVersions || !timeRef.IsZero()) { fatalIf(errInvalidArgument().Trace(args...), "Zip file listing can only be performed on the latest version") } @@ -189,6 +201,7 @@ func checkListSyntax(cliCtx *cli.Context) ([]string, doListOptions) { withVersions: withVersions, listZip: listZip, filter: storageClasss, + sortBy: sortBy, } return args, opts } diff --git a/cmd/ls.go b/cmd/ls.go index fe23bfc6ef..f7845eabd7 100644 --- a/cmd/ls.go +++ b/cmd/ls.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "path/filepath" + "slices" "sort" "strings" "time" @@ -198,12 +199,10 @@ func (s summaryMessage) JSON() string { } // Pretty print the list of versions belonging to one object -func printObjectVersions(clntURL ClientURL, ctntVersions []*ClientContent, printAllVersions bool) { +func getObjectVersions(clntURL ClientURL, ctntVersions []*ClientContent, printAllVersions bool) []contentMessage { sortObjectVersions(ctntVersions) msgs := generateContentMessages(clntURL, ctntVersions, printAllVersions) - for _, msg := range msgs { - printMsg(msg) - } + return msgs } type doListOptions struct { @@ -214,10 +213,11 @@ type doListOptions struct { withVersions bool listZip bool filter string + sortBy string } -// doList - list all entities inside a folder. -func doList(ctx context.Context, clnt Client, o doListOptions) error { +// Will loop over entities listed and call fn() for every object with all their versions +func loopOverObjects(ctx context.Context, clnt Client, fn func(content *[]contentMessage) error, o doListOptions) (int64, int64, error) { var ( lastPath string perObjectVersions []*ClientContent @@ -245,9 +245,11 @@ func doList(ctx context.Context, clnt Client, o doListOptions) error { continue } + // Check if we have moved to a new object (or if this is another version of the last iteration's object) if lastPath != content.URL.Path { // Print any object in the current list before reinitializing it - printObjectVersions(clnt.GetURL(), perObjectVersions, o.withVersions) + objects := getObjectVersions(clnt.GetURL(), perObjectVersions, o.withVersions) + fn(&objects) lastPath = content.URL.Path perObjectVersions = []*ClientContent{} } @@ -257,7 +259,56 @@ func doList(ctx context.Context, clnt Client, o doListOptions) error { totalObjects++ } - printObjectVersions(clnt.GetURL(), perObjectVersions, o.withVersions) + objects := getObjectVersions(clnt.GetURL(), perObjectVersions, o.withVersions) + fn(&objects) + return totalObjects, totalSize, cErr +} + +// doList - list all entities inside a folder. +func doList(ctx context.Context, clnt Client, o doListOptions) error { + var cErr error + isAccumulating := o.sortBy == "size" // should objects be accumulated instead of printed directly + var accumulatedObjects []contentMessage + + var processFn func(objects *[]contentMessage) error + if isAccumulating { + processFn = func(newObjects *[]contentMessage) error { + // Accumulate all objects for sorting later + accumulatedObjects = append(accumulatedObjects, *newObjects...) + return nil + } + } else { + processFn = func(objects *[]contentMessage) error { + // Print objects as they come + for _, obj := range *objects { + printMsg(obj) + } + return nil + } + } + + totalObjects, totalSize, cErr := loopOverObjects(ctx, clnt, processFn, o) + + // if isAccumulating is true, objects have been accumulated instead of printed + if isAccumulating { + // Sort if requested + switch o.sortBy { + case "size": + slices.SortFunc(accumulatedObjects, func(a, b contentMessage) int { + if a.Size < b.Size { + return -1 + } else if a.Size > b.Size { + return 1 + } + return 0 + }) + } + + // Print all objects + for _, obj := range accumulatedObjects { + printMsg(obj) + } + } if o.isSummary { printMsg(summaryMessage{