lsf.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Package lsf provides the lsf command.
  2. package lsf
  3. import (
  4. "context"
  5. "fmt"
  6. "io"
  7. "os"
  8. "github.com/rclone/rclone/cmd"
  9. "github.com/rclone/rclone/cmd/ls/lshelp"
  10. "github.com/rclone/rclone/fs"
  11. "github.com/rclone/rclone/fs/config/flags"
  12. "github.com/rclone/rclone/fs/hash"
  13. "github.com/rclone/rclone/fs/operations"
  14. "github.com/spf13/cobra"
  15. )
  16. var (
  17. format string
  18. timeFormat string
  19. separator string
  20. dirSlash bool
  21. recurse bool
  22. hashType = hash.MD5
  23. filesOnly bool
  24. dirsOnly bool
  25. csv bool
  26. absolute bool
  27. )
  28. func init() {
  29. cmd.Root.AddCommand(commandDefinition)
  30. cmdFlags := commandDefinition.Flags()
  31. flags.StringVarP(cmdFlags, &format, "format", "F", "p", "Output format - see help for details", "")
  32. flags.StringVarP(cmdFlags, &timeFormat, "time-format", "t", "", "Specify a custom time format, or 'max' for max precision supported by remote (default: 2006-01-02 15:04:05)", "")
  33. flags.StringVarP(cmdFlags, &separator, "separator", "s", ";", "Separator for the items in the format", "")
  34. flags.BoolVarP(cmdFlags, &dirSlash, "dir-slash", "d", true, "Append a slash to directory names", "")
  35. flags.FVarP(cmdFlags, &hashType, "hash", "", "Use this hash when `h` is used in the format MD5|SHA-1|DropboxHash", "")
  36. flags.BoolVarP(cmdFlags, &filesOnly, "files-only", "", false, "Only list files", "")
  37. flags.BoolVarP(cmdFlags, &dirsOnly, "dirs-only", "", false, "Only list directories", "")
  38. flags.BoolVarP(cmdFlags, &csv, "csv", "", false, "Output in CSV format", "")
  39. flags.BoolVarP(cmdFlags, &absolute, "absolute", "", false, "Put a leading / in front of path names", "")
  40. flags.BoolVarP(cmdFlags, &recurse, "recursive", "R", false, "Recurse into the listing", "")
  41. }
  42. var commandDefinition = &cobra.Command{
  43. Use: "lsf remote:path",
  44. Short: `List directories and objects in remote:path formatted for parsing.`,
  45. Long: `
  46. List the contents of the source path (directories and objects) to
  47. standard output in a form which is easy to parse by scripts. By
  48. default this will just be the names of the objects and directories,
  49. one per line. The directories will have a / suffix.
  50. Eg
  51. $ rclone lsf swift:bucket
  52. bevajer5jef
  53. canole
  54. diwogej7
  55. ferejej3gux/
  56. fubuwic
  57. Use the ` + "`--format`" + ` option to control what gets listed. By default this
  58. is just the path, but you can use these parameters to control the
  59. output:
  60. p - path
  61. s - size
  62. t - modification time
  63. h - hash
  64. i - ID of object
  65. o - Original ID of underlying object
  66. m - MimeType of object if known
  67. e - encrypted name
  68. T - tier of storage if known, e.g. "Hot" or "Cool"
  69. M - Metadata of object in JSON blob format, eg {"key":"value"}
  70. So if you wanted the path, size and modification time, you would use
  71. ` + "`--format \"pst\"`, or maybe `--format \"tsp\"`" + ` to put the path last.
  72. Eg
  73. $ rclone lsf --format "tsp" swift:bucket
  74. 2016-06-25 18:55:41;60295;bevajer5jef
  75. 2016-06-25 18:55:43;90613;canole
  76. 2016-06-25 18:55:43;94467;diwogej7
  77. 2018-04-26 08:50:45;0;ferejej3gux/
  78. 2016-06-25 18:55:40;37600;fubuwic
  79. If you specify "h" in the format you will get the MD5 hash by default,
  80. use the ` + "`--hash`" + ` flag to change which hash you want. Note that this
  81. can be returned as an empty string if it isn't available on the object
  82. (and for directories), "ERROR" if there was an error reading it from
  83. the object and "UNSUPPORTED" if that object does not support that hash
  84. type.
  85. For example, to emulate the md5sum command you can use
  86. rclone lsf -R --hash MD5 --format hp --separator " " --files-only .
  87. Eg
  88. $ rclone lsf -R --hash MD5 --format hp --separator " " --files-only swift:bucket
  89. 7908e352297f0f530b84a756f188baa3 bevajer5jef
  90. cd65ac234e6fea5925974a51cdd865cc canole
  91. 03b5341b4f234b9d984d03ad076bae91 diwogej7
  92. 8fd37c3810dd660778137ac3a66cc06d fubuwic
  93. 99713e14a4c4ff553acaf1930fad985b gixacuh7ku
  94. (Though "rclone md5sum ." is an easier way of typing this.)
  95. By default the separator is ";" this can be changed with the
  96. ` + "`--separator`" + ` flag. Note that separators aren't escaped in the path so
  97. putting it last is a good strategy.
  98. Eg
  99. $ rclone lsf --separator "," --format "tshp" swift:bucket
  100. 2016-06-25 18:55:41,60295,7908e352297f0f530b84a756f188baa3,bevajer5jef
  101. 2016-06-25 18:55:43,90613,cd65ac234e6fea5925974a51cdd865cc,canole
  102. 2016-06-25 18:55:43,94467,03b5341b4f234b9d984d03ad076bae91,diwogej7
  103. 2018-04-26 08:52:53,0,,ferejej3gux/
  104. 2016-06-25 18:55:40,37600,8fd37c3810dd660778137ac3a66cc06d,fubuwic
  105. You can output in CSV standard format. This will escape things in "
  106. if they contain ,
  107. Eg
  108. $ rclone lsf --csv --files-only --format ps remote:path
  109. test.log,22355
  110. test.sh,449
  111. "this file contains a comma, in the file name.txt",6
  112. Note that the ` + "`--absolute`" + ` parameter is useful for making lists of files
  113. to pass to an rclone copy with the ` + "`--files-from-raw`" + ` flag.
  114. For example, to find all the files modified within one day and copy
  115. those only (without traversing the whole directory structure):
  116. rclone lsf --absolute --files-only --max-age 1d /path/to/local > new_files
  117. rclone copy --files-from-raw new_files /path/to/local remote:path
  118. The default time format is ` + "`'2006-01-02 15:04:05'`" + `.
  119. [Other formats](https://pkg.go.dev/time#pkg-constants) can be specified with the ` + "`--time-format`" + ` flag.
  120. Examples:
  121. rclone lsf remote:path --format pt --time-format 'Jan 2, 2006 at 3:04pm (MST)'
  122. rclone lsf remote:path --format pt --time-format '2006-01-02 15:04:05.000000000'
  123. rclone lsf remote:path --format pt --time-format '2006-01-02T15:04:05.999999999Z07:00'
  124. rclone lsf remote:path --format pt --time-format RFC3339
  125. rclone lsf remote:path --format pt --time-format DateOnly
  126. rclone lsf remote:path --format pt --time-format max
  127. ` + "`--time-format max`" + ` will automatically truncate ` + "'`2006-01-02 15:04:05.000000000`'" + `
  128. to the maximum precision supported by the remote.
  129. ` + lshelp.Help,
  130. Annotations: map[string]string{
  131. "versionIntroduced": "v1.40",
  132. "groups": "Filter,Listing",
  133. },
  134. Run: func(command *cobra.Command, args []string) {
  135. cmd.CheckArgs(1, 1, command, args)
  136. fsrc := cmd.NewFsSrc(args)
  137. cmd.Run(false, false, command, func() error {
  138. // Work out if the separatorFlag was supplied or not
  139. separatorFlag := command.Flags().Lookup("separator")
  140. separatorFlagSupplied := separatorFlag != nil && separatorFlag.Changed
  141. // Default the separator to , if using CSV
  142. if csv && !separatorFlagSupplied {
  143. separator = ","
  144. }
  145. return Lsf(context.Background(), fsrc, os.Stdout)
  146. })
  147. },
  148. }
  149. // Lsf lists all the objects in the path with modification time, size
  150. // and path in specific format.
  151. func Lsf(ctx context.Context, fsrc fs.Fs, out io.Writer) error {
  152. var list operations.ListFormat
  153. list.SetSeparator(separator)
  154. list.SetCSV(csv)
  155. list.SetDirSlash(dirSlash)
  156. list.SetAbsolute(absolute)
  157. var opt = operations.ListJSONOpt{
  158. NoModTime: true,
  159. NoMimeType: true,
  160. DirsOnly: dirsOnly,
  161. FilesOnly: filesOnly,
  162. Recurse: recurse,
  163. }
  164. for _, char := range format {
  165. switch char {
  166. case 'p':
  167. list.AddPath()
  168. case 't':
  169. if timeFormat == "max" {
  170. timeFormat = operations.FormatForLSFPrecision(fsrc.Precision())
  171. }
  172. list.AddModTime(timeFormat)
  173. opt.NoModTime = false
  174. case 's':
  175. list.AddSize()
  176. case 'h':
  177. list.AddHash(hashType)
  178. opt.ShowHash = true
  179. opt.HashTypes = []string{hashType.String()}
  180. case 'i':
  181. list.AddID()
  182. case 'm':
  183. list.AddMimeType()
  184. opt.NoMimeType = false
  185. case 'e':
  186. list.AddEncrypted()
  187. opt.ShowEncrypted = true
  188. case 'o':
  189. list.AddOrigID()
  190. opt.ShowOrigIDs = true
  191. case 'T':
  192. list.AddTier()
  193. case 'M':
  194. list.AddMetadata()
  195. opt.Metadata = true
  196. default:
  197. return fmt.Errorf("unknown format character %q", char)
  198. }
  199. }
  200. return operations.ListJSON(ctx, fsrc, "", &opt, func(item *operations.ListJSONItem) error {
  201. // Make size deterministic for tests
  202. if item.IsDir {
  203. item.Size = -1
  204. }
  205. _, _ = fmt.Fprintln(out, list.Format(item))
  206. return nil
  207. })
  208. }