github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/cmd/lsjson/lsjson.go (about) 1 // Package lsjson provides the lsjson command. 2 package lsjson 3 4 import ( 5 "context" 6 "encoding/json" 7 "fmt" 8 "os" 9 10 "github.com/rclone/rclone/cmd" 11 "github.com/rclone/rclone/cmd/ls/lshelp" 12 "github.com/rclone/rclone/fs" 13 "github.com/rclone/rclone/fs/config/flags" 14 "github.com/rclone/rclone/fs/operations" 15 "github.com/spf13/cobra" 16 ) 17 18 var ( 19 opt operations.ListJSONOpt 20 statOnly bool 21 ) 22 23 func init() { 24 cmd.Root.AddCommand(commandDefinition) 25 cmdFlags := commandDefinition.Flags() 26 flags.BoolVarP(cmdFlags, &opt.Recurse, "recursive", "R", false, "Recurse into the listing", "") 27 flags.BoolVarP(cmdFlags, &opt.ShowHash, "hash", "", false, "Include hashes in the output (may take longer)", "") 28 flags.BoolVarP(cmdFlags, &opt.NoModTime, "no-modtime", "", false, "Don't read the modification time (can speed things up)", "") 29 flags.BoolVarP(cmdFlags, &opt.NoMimeType, "no-mimetype", "", false, "Don't read the mime type (can speed things up)", "") 30 flags.BoolVarP(cmdFlags, &opt.ShowEncrypted, "encrypted", "", false, "Show the encrypted names", "") 31 flags.BoolVarP(cmdFlags, &opt.ShowOrigIDs, "original", "", false, "Show the ID of the underlying Object", "") 32 flags.BoolVarP(cmdFlags, &opt.FilesOnly, "files-only", "", false, "Show only files in the listing", "") 33 flags.BoolVarP(cmdFlags, &opt.DirsOnly, "dirs-only", "", false, "Show only directories in the listing", "") 34 flags.BoolVarP(cmdFlags, &opt.Metadata, "metadata", "M", false, "Add metadata to the listing", "") 35 flags.StringArrayVarP(cmdFlags, &opt.HashTypes, "hash-type", "", nil, "Show only this hash type (may be repeated)", "") 36 flags.BoolVarP(cmdFlags, &statOnly, "stat", "", false, "Just return the info for the pointed to file", "") 37 } 38 39 var commandDefinition = &cobra.Command{ 40 Use: "lsjson remote:path", 41 Short: `List directories and objects in the path in JSON format.`, 42 Long: `List directories and objects in the path in JSON format. 43 44 The output is an array of Items, where each Item looks like this 45 46 { 47 "Hashes" : { 48 "SHA-1" : "f572d396fae9206628714fb2ce00f72e94f2258f", 49 "MD5" : "b1946ac92492d2347c6235b4d2611184", 50 "DropboxHash" : "ecb65bb98f9d905b70458986c39fcbad7715e5f2fcc3b1f07767d7c83e2438cc" 51 }, 52 "ID": "y2djkhiujf83u33", 53 "OrigID": "UYOJVTUW00Q1RzTDA", 54 "IsBucket" : false, 55 "IsDir" : false, 56 "MimeType" : "application/octet-stream", 57 "ModTime" : "2017-05-31T16:15:57.034468261+01:00", 58 "Name" : "file.txt", 59 "Encrypted" : "v0qpsdq8anpci8n929v3uu9338", 60 "EncryptedPath" : "kja9098349023498/v0qpsdq8anpci8n929v3uu9338", 61 "Path" : "full/path/goes/here/file.txt", 62 "Size" : 6, 63 "Tier" : "hot", 64 } 65 66 If ` + "`--hash`" + ` is not specified, the Hashes property will be omitted. The 67 types of hash can be specified with the ` + "`--hash-type`" + ` parameter (which 68 may be repeated). If ` + "`--hash-type`" + ` is set then it implies ` + "`--hash`" + `. 69 70 If ` + "`--no-modtime`" + ` is specified then ModTime will be blank. This can 71 speed things up on remotes where reading the ModTime takes an extra 72 request (e.g. s3, swift). 73 74 If ` + "`--no-mimetype`" + ` is specified then MimeType will be blank. This can 75 speed things up on remotes where reading the MimeType takes an extra 76 request (e.g. s3, swift). 77 78 If ` + "`--encrypted`" + ` is not specified the Encrypted will be omitted. 79 80 If ` + "`--dirs-only`" + ` is not specified files in addition to directories are 81 returned 82 83 If ` + "`--files-only`" + ` is not specified directories in addition to the files 84 will be returned. 85 86 If ` + "`--metadata`" + ` is set then an additional Metadata key will be returned. 87 This will have metadata in rclone standard format as a JSON object. 88 89 if ` + "`--stat`" + ` is set then a single JSON blob will be returned about the 90 item pointed to. This will return an error if the item isn't found. 91 However on bucket based backends (like s3, gcs, b2, azureblob etc) if 92 the item isn't found it will return an empty directory as it isn't 93 possible to tell empty directories from missing directories there. 94 95 The Path field will only show folders below the remote path being listed. 96 If "remote:path" contains the file "subfolder/file.txt", the Path for "file.txt" 97 will be "subfolder/file.txt", not "remote:path/subfolder/file.txt". 98 When used without ` + "`--recursive`" + ` the Path will always be the same as Name. 99 100 If the directory is a bucket in a bucket-based backend, then 101 "IsBucket" will be set to true. This key won't be present unless it is 102 "true". 103 104 The time is in RFC3339 format with up to nanosecond precision. The 105 number of decimal digits in the seconds will depend on the precision 106 that the remote can hold the times, so if times are accurate to the 107 nearest millisecond (e.g. Google Drive) then 3 digits will always be 108 shown ("2017-05-31T16:15:57.034+01:00") whereas if the times are 109 accurate to the nearest second (Dropbox, Box, WebDav, etc.) no digits 110 will be shown ("2017-05-31T16:15:57+01:00"). 111 112 The whole output can be processed as a JSON blob, or alternatively it 113 can be processed line by line as each item is written one to a line. 114 ` + lshelp.Help, 115 Annotations: map[string]string{ 116 "versionIntroduced": "v1.37", 117 "groups": "Filter,Listing", 118 }, 119 RunE: func(command *cobra.Command, args []string) error { 120 // Make sure we set the global Metadata flag too as it 121 // isn't parsed by cobra. We need to do this first 122 // before any backends are created. 123 ci := fs.GetConfig(context.Background()) 124 ci.Metadata = opt.Metadata 125 126 cmd.CheckArgs(1, 1, command, args) 127 var fsrc fs.Fs 128 var remote string 129 if statOnly { 130 fsrc, remote = cmd.NewFsFile(args[0]) 131 } else { 132 fsrc = cmd.NewFsSrc(args) 133 } 134 cmd.Run(false, false, command, func() error { 135 if statOnly { 136 item, err := operations.StatJSON(context.Background(), fsrc, remote, &opt) 137 if err != nil { 138 return err 139 } 140 out, err := json.MarshalIndent(item, "", "\t") 141 if err != nil { 142 return fmt.Errorf("failed to marshal list object: %w", err) 143 } 144 _, err = os.Stdout.Write(out) 145 if err != nil { 146 return fmt.Errorf("failed to write to output: %w", err) 147 } 148 fmt.Println() 149 } else { 150 fmt.Println("[") 151 first := true 152 err := operations.ListJSON(context.Background(), fsrc, remote, &opt, func(item *operations.ListJSONItem) error { 153 out, err := json.Marshal(item) 154 if err != nil { 155 return fmt.Errorf("failed to marshal list object: %w", err) 156 } 157 if first { 158 first = false 159 } else { 160 fmt.Print(",\n") 161 } 162 _, err = os.Stdout.Write(out) 163 if err != nil { 164 return fmt.Errorf("failed to write to output: %w", err) 165 } 166 return nil 167 }) 168 if err != nil { 169 return err 170 } 171 if !first { 172 fmt.Println() 173 } 174 fmt.Println("]") 175 } 176 return nil 177 }) 178 return nil 179 }, 180 }