feat(globs): glob support extended to ls and documented in README
This commit is contained in:
parent
badbf3b6bb
commit
95c9ac8fca
8 changed files with 316 additions and 74 deletions
146
README.md
146
README.md
|
|
@ -34,6 +34,7 @@
|
||||||
- [Installation](https://github.com/Llywelwyn/pda#installation)
|
- [Installation](https://github.com/Llywelwyn/pda#installation)
|
||||||
- [Get Started](https://github.com/Llywelwyn/pda#get-started)
|
- [Get Started](https://github.com/Llywelwyn/pda#get-started)
|
||||||
- [Templates](https://github.com/Llywelwyn/pda#templates)
|
- [Templates](https://github.com/Llywelwyn/pda#templates)
|
||||||
|
- [Globs](https://github.com/Llywelwyn/pda#globs)
|
||||||
- [Secrets](https://github.com/Llywelwyn/pda#secrets)
|
- [Secrets](https://github.com/Llywelwyn/pda#secrets)
|
||||||
- [TTL](https://github.com/Llywelwyn/pda#ttl)
|
- [TTL](https://github.com/Llywelwyn/pda#ttl)
|
||||||
- [Binary](https://github.com/Llywelwyn/pda#binary)
|
- [Binary](https://github.com/Llywelwyn/pda#binary)
|
||||||
|
|
@ -129,15 +130,22 @@ pda del kitty
|
||||||
# remove "kitty": are you sure? [y/n]
|
# remove "kitty": are you sure? [y/n]
|
||||||
# y
|
# y
|
||||||
|
|
||||||
|
# Or skip the prompt.
|
||||||
|
pda del kitty --force
|
||||||
|
|
||||||
|
# Remove multiple keys, within the same or different stores.
|
||||||
pda del kitty dog@animals
|
pda del kitty dog@animals
|
||||||
# remove "kitty", "dog@animals": are you sure? [y/n]
|
# remove "kitty", "dog@animals": are you sure? [y/n]
|
||||||
# y
|
# y
|
||||||
|
|
||||||
# Or skip the prompt.
|
# Mix exact keys with globs.
|
||||||
pda del kitty --force
|
pda set cog "cogs"
|
||||||
|
pda set dog "doggy"
|
||||||
# Delete many in one go.
|
pda set kitty "cat"
|
||||||
pda del kitty dogs cats --force
|
pda del kitty --glob ?og
|
||||||
|
# remove "kitty", "cog", "dog": are you sure? [y/n]
|
||||||
|
# y
|
||||||
|
# Default glob separators: "/-_.@:". Override with --glob-sep.
|
||||||
```
|
```
|
||||||
|
|
||||||
<p align="center"></p><!-- spacer -->
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
@ -300,6 +308,134 @@ pda get hello --no-template
|
||||||
|
|
||||||
<p align="center"></p><!-- spacer -->
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
### Globs
|
||||||
|
|
||||||
|
Globs can be used in a few commands where their use makes sense. `gobwas/glob` is used for matching.
|
||||||
|
|
||||||
|
Searching for globs is inherently slower than looking for direct matches, so globs are opt-in via a repeatable `--glob/-g` flag by default rather than having every string treated as a glob by default. Realistically the performance impact will be negligible unless you have many thousands of entries in the same database.
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
`*` wildcards a word or series of characters.
|
||||||
|
```bash
|
||||||
|
pda ls --no-values
|
||||||
|
# cat
|
||||||
|
# dog
|
||||||
|
# cog
|
||||||
|
# mouse hotdog
|
||||||
|
# mouse house
|
||||||
|
# foo.bar.baz
|
||||||
|
|
||||||
|
pda ls --glob "*"
|
||||||
|
# cat
|
||||||
|
# dog
|
||||||
|
# cog
|
||||||
|
|
||||||
|
pda ls --glob "* *"
|
||||||
|
# mouse hotdog
|
||||||
|
# mouse house
|
||||||
|
|
||||||
|
pda ls --glob "foo.*.baz"
|
||||||
|
# foo.bar.baz
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
`**` super-wildcards ignore word boundaries.
|
||||||
|
```bash
|
||||||
|
pda ls --glob "foo**"
|
||||||
|
# foo.bar.baz
|
||||||
|
|
||||||
|
pda ls --glob "**g"
|
||||||
|
# dog
|
||||||
|
# cog
|
||||||
|
# mouse hotdog
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
`?` wildcards a single letter.
|
||||||
|
```bash
|
||||||
|
pda ls --glob ?og
|
||||||
|
# dog
|
||||||
|
# cog
|
||||||
|
# frog --> fail
|
||||||
|
# dogs --> fail
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
`[abc]` must match one of the characters in the brackets.
|
||||||
|
```bash
|
||||||
|
pda ls --glob [dc]og
|
||||||
|
# dog
|
||||||
|
# cog
|
||||||
|
# bog --> fail
|
||||||
|
|
||||||
|
# Can be negated with '!'
|
||||||
|
pda ls --glob [!dc]og
|
||||||
|
# dog --> fail
|
||||||
|
# cog --> fail
|
||||||
|
# bog
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
`[a-c]` must fall within the range given in the brackets
|
||||||
|
```bash
|
||||||
|
pda ls --glob [a-g]ag
|
||||||
|
# bag
|
||||||
|
# gag
|
||||||
|
# wag --> fail
|
||||||
|
|
||||||
|
# Can be negated with '!'
|
||||||
|
pda ls --glob [!a-g]ag
|
||||||
|
# bag --> fail
|
||||||
|
# gag --> fail
|
||||||
|
# wag
|
||||||
|
|
||||||
|
pda ls --glob 19[90-99]
|
||||||
|
# 1991
|
||||||
|
# 1992
|
||||||
|
# 2001 --> fail
|
||||||
|
# 1988 --> fail
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
Globs can be arbitrarily complex, and can be combined with strict matches.
|
||||||
|
```bash
|
||||||
|
pda ls --no-keys
|
||||||
|
# cat
|
||||||
|
# mouse trap
|
||||||
|
# dog house
|
||||||
|
# cat flap
|
||||||
|
# cogwheel
|
||||||
|
|
||||||
|
pda rm cat --glob "{mouse,[cd]og}**"
|
||||||
|
# remove: 'cat', 'mouse trap', 'dog house', 'cogwheel': are you sure? [y/n]
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
|
`--glob-sep` can be used to change the default list of separators used to determine word boundaries. Separators default to a somewhat reasonable list of common alphanumeric characters so should be usable in most usual situations.
|
||||||
|
```bash
|
||||||
|
pda ls --no-keys
|
||||||
|
# foo%baz
|
||||||
|
|
||||||
|
pda ls --glob "*"
|
||||||
|
# foo%baz
|
||||||
|
|
||||||
|
pda ls --glob "*" --glob-sep "%"
|
||||||
|
# foo%baz --> fail
|
||||||
|
# % is considered a word boundary, so "*" no longer matches.
|
||||||
|
|
||||||
|
pda ls --glob "*%*" --glob-sep "%"
|
||||||
|
# foo%baz
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center"></p><!-- spacer -->
|
||||||
|
|
||||||
### Secrets
|
### Secrets
|
||||||
|
|
||||||
Mark sensitive values with `secret` to stop accidents.
|
Mark sensitive values with `secret` to stop accidents.
|
||||||
|
|
|
||||||
104
cmd/del.go
104
cmd/del.go
|
|
@ -36,7 +36,7 @@ var delCmd = &cobra.Command{
|
||||||
Use: "del KEY[@DB] [KEY[@DB] ...]",
|
Use: "del KEY[@DB] [KEY[@DB] ...]",
|
||||||
Short: "Delete one or more keys. Optionally specify a db.",
|
Short: "Delete one or more keys. Optionally specify a db.",
|
||||||
Aliases: []string{"delete", "rm", "remove"},
|
Aliases: []string{"delete", "rm", "remove"},
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.ArbitraryArgs,
|
||||||
RunE: del,
|
RunE: del,
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
}
|
}
|
||||||
|
|
@ -48,12 +48,20 @@ func del(cmd *cobra.Command, args []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
useGlob, err := cmd.Flags().GetBool("glob")
|
globPatterns, err := cmd.Flags().GetStringSlice("glob")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
separators, err := parseGlobSeparators(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
targetKeys, deleteTargets, err := resolveDeleteTargets(store, args, useGlob)
|
if len(args) == 0 && len(globPatterns) == 0 {
|
||||||
|
return fmt.Errorf("cannot remove: no keys provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetKeys, deleteTargets, err := resolveDeleteTargets(store, args, globPatterns, separators)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +108,8 @@ func del(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
delCmd.Flags().BoolP("force", "f", false, "Force delete without confirmation")
|
delCmd.Flags().BoolP("force", "f", false, "Force delete without confirmation")
|
||||||
delCmd.Flags().BoolP("glob", "g", false, "Treat KEY arguments as glob patterns")
|
delCmd.Flags().StringSliceP("glob", "g", nil, "Delete keys matching glob pattern (repeatable)")
|
||||||
|
delCmd.Flags().String("glob-sep", "", fmt.Sprintf("Characters treated as separators for globbing (default %q)", defaultGlobSeparatorsDisplay()))
|
||||||
rootCmd.AddCommand(delCmd)
|
rootCmd.AddCommand(delCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,49 +148,55 @@ func formatKeyForPrompt(store *Store, arg string) (string, error) {
|
||||||
return fmt.Sprintf("%s@%s", arg, db), nil
|
return fmt.Sprintf("%s@%s", arg, db), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveDeleteTargets(store *Store, args []string, useGlob bool) ([]string, []string, error) {
|
func resolveDeleteTargets(store *Store, exactArgs []string, globPatterns []string, separators []rune) ([]string, []string, error) {
|
||||||
if !useGlob {
|
targetSet := make(map[string]struct{})
|
||||||
targetKeys := make([]string, 0, len(args))
|
var targetKeys []string
|
||||||
for _, arg := range args {
|
var deleteTargets []string
|
||||||
exists, err := keyExists(store, arg)
|
|
||||||
if err != nil {
|
for _, arg := range exactArgs {
|
||||||
return nil, nil, fmt.Errorf("cannot remove '%s': %v", arg, err)
|
exists, err := keyExists(store, arg)
|
||||||
}
|
if err != nil {
|
||||||
if !exists {
|
return nil, nil, fmt.Errorf("cannot remove '%s': %v", arg, err)
|
||||||
return nil, nil, fmt.Errorf("cannot remove '%s': No such key", arg)
|
|
||||||
}
|
|
||||||
targetKey, err := formatKeyForPrompt(store, arg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
targetKeys = append(targetKeys, targetKey)
|
|
||||||
}
|
}
|
||||||
return targetKeys, args, nil
|
if !exists {
|
||||||
|
return nil, nil, fmt.Errorf("cannot remove '%s': No such key", arg)
|
||||||
|
}
|
||||||
|
formatted, err := formatKeyForPrompt(store, arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if _, seen := targetSet[arg]; !seen {
|
||||||
|
targetSet[arg] = struct{}{}
|
||||||
|
targetKeys = append(targetKeys, formatted)
|
||||||
|
deleteTargets = append(deleteTargets, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(globPatterns) == 0 {
|
||||||
|
return targetKeys, deleteTargets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type compiledPattern struct {
|
type compiledPattern struct {
|
||||||
rawArg string
|
rawArg string
|
||||||
db string
|
db string
|
||||||
matcher glob.Glob
|
matcher glob.Glob
|
||||||
pattern string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var compiled []compiledPattern
|
var compiled []compiledPattern
|
||||||
for _, arg := range args {
|
for _, raw := range globPatterns {
|
||||||
kb, db, err := store.parse(arg, true)
|
kb, db, err := store.parse(raw, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
pattern := string(kb)
|
pattern := string(kb)
|
||||||
m, err := glob.Compile(pattern)
|
m, err := glob.Compile(pattern, separators...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("cannot remove '%s': %v", arg, err)
|
return nil, nil, fmt.Errorf("cannot remove '%s': %v", raw, err)
|
||||||
}
|
}
|
||||||
compiled = append(compiled, compiledPattern{
|
compiled = append(compiled, compiledPattern{
|
||||||
rawArg: arg,
|
rawArg: raw,
|
||||||
db: db,
|
db: db,
|
||||||
matcher: m,
|
matcher: m,
|
||||||
pattern: pattern,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,36 +213,31 @@ func resolveDeleteTargets(store *Store, args []string, useGlob bool) ([]string,
|
||||||
return keys, nil
|
return keys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
targetSet := make(map[string]struct{})
|
|
||||||
var targetKeys []string
|
|
||||||
var deleteTargets []string
|
|
||||||
|
|
||||||
for _, p := range compiled {
|
for _, p := range compiled {
|
||||||
keys, err := getKeys(p.db)
|
keys, err := getKeys(p.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("cannot remove '%s': %v", p.rawArg, err)
|
return nil, nil, fmt.Errorf("cannot remove '%s': %v", p.rawArg, err)
|
||||||
}
|
}
|
||||||
var matched []string
|
var matched bool
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if p.matcher.Match(k) {
|
if p.matcher.Match(k) {
|
||||||
matched = append(matched, fmt.Sprintf("%s@%s", k, p.db))
|
matched = true
|
||||||
|
full := fmt.Sprintf("%s@%s", k, p.db)
|
||||||
|
if _, seen := targetSet[full]; seen {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
targetSet[full] = struct{}{}
|
||||||
|
display, err := formatKeyForPrompt(store, full)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
targetKeys = append(targetKeys, display)
|
||||||
|
deleteTargets = append(deleteTargets, full)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(matched) == 0 {
|
if !matched {
|
||||||
return nil, nil, fmt.Errorf("cannot remove '%s': No matches for pattern", p.rawArg)
|
return nil, nil, fmt.Errorf("cannot remove '%s': No matches for pattern", p.rawArg)
|
||||||
}
|
}
|
||||||
for _, full := range matched {
|
|
||||||
if _, seen := targetSet[full]; seen {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
targetSet[full] = struct{}{}
|
|
||||||
display, err := formatKeyForPrompt(store, full)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
targetKeys = append(targetKeys, display)
|
|
||||||
deleteTargets = append(deleteTargets, full)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetKeys, deleteTargets, nil
|
return targetKeys, deleteTargets, nil
|
||||||
|
|
|
||||||
28
cmd/glob.go
Normal file
28
cmd/glob.go
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultGlobSeparators = []rune{'/', '-', '_', '.', '@', ':', ' '}
|
||||||
|
|
||||||
|
func defaultGlobSeparatorsDisplay() string {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, r := range defaultGlobSeparators {
|
||||||
|
b.WriteRune(r)
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGlobSeparators(cmd *cobra.Command) ([]rune, error) {
|
||||||
|
sepStr, err := cmd.Flags().GetString("glob-sep")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if sepStr == "" {
|
||||||
|
return defaultGlobSeparators, nil
|
||||||
|
}
|
||||||
|
return []rune(sepStr), nil
|
||||||
|
}
|
||||||
42
cmd/list.go
42
cmd/list.go
|
|
@ -24,8 +24,10 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/dgraph-io/badger/v4"
|
"github.com/dgraph-io/badger/v4"
|
||||||
|
"github.com/gobwas/glob"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
@ -63,6 +65,35 @@ func list(cmd *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("cannot ls '%s': %v", targetDB, err)
|
return fmt.Errorf("cannot ls '%s': %v", targetDB, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
globPatterns, err := cmd.Flags().GetStringSlice("glob")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot ls '%s': %v", targetDB, err)
|
||||||
|
}
|
||||||
|
separators, err := parseGlobSeparators(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot ls '%s': %v", targetDB, err)
|
||||||
|
}
|
||||||
|
var matchers []glob.Glob
|
||||||
|
for _, pattern := range globPatterns {
|
||||||
|
m, err := glob.Compile(strings.ToLower(pattern), separators...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot ls '%s': %v", targetDB, err)
|
||||||
|
}
|
||||||
|
matchers = append(matchers, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
matchesKey := func(k string) bool {
|
||||||
|
if len(matchers) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, m := range matchers {
|
||||||
|
if m.Match(k) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
columnKinds, err := requireColumns(flags)
|
columnKinds, err := requireColumns(flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot ls '%s': %v", targetDB, err)
|
return fmt.Errorf("cannot ls '%s': %v", targetDB, err)
|
||||||
|
|
@ -89,6 +120,7 @@ func list(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
placeholder := "**********"
|
placeholder := "**********"
|
||||||
|
var matchedCount int
|
||||||
trans := TransactionArgs{
|
trans := TransactionArgs{
|
||||||
key: targetDB,
|
key: targetDB,
|
||||||
readonly: true,
|
readonly: true,
|
||||||
|
|
@ -103,6 +135,10 @@ func list(cmd *cobra.Command, args []string) error {
|
||||||
for it.Rewind(); it.Valid(); it.Next() {
|
for it.Rewind(); it.Valid(); it.Next() {
|
||||||
item := it.Item()
|
item := it.Item()
|
||||||
key := string(item.KeyCopy(nil))
|
key := string(item.KeyCopy(nil))
|
||||||
|
if !matchesKey(key) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matchedCount++
|
||||||
meta := item.UserMeta()
|
meta := item.UserMeta()
|
||||||
isSecret := meta&metaSecret != 0
|
isSecret := meta&metaSecret != 0
|
||||||
|
|
||||||
|
|
@ -143,6 +179,10 @@ func list(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(matchers) > 0 && matchedCount == 0 {
|
||||||
|
return fmt.Errorf("cannot ls '%s': No matches for pattern", targetDB)
|
||||||
|
}
|
||||||
|
|
||||||
applyColumnConstraints(tw, columnKinds, output, maxContentWidths)
|
applyColumnConstraints(tw, columnKinds, output, maxContentWidths)
|
||||||
|
|
||||||
flags.render(tw)
|
flags.render(tw)
|
||||||
|
|
@ -157,5 +197,7 @@ func init() {
|
||||||
listCmd.Flags().BoolVarP(&ttl, "ttl", "t", false, "append a TTL column when entries expire")
|
listCmd.Flags().BoolVarP(&ttl, "ttl", "t", false, "append a TTL column when entries expire")
|
||||||
listCmd.Flags().BoolVar(&header, "header", false, "include header row")
|
listCmd.Flags().BoolVar(&header, "header", false, "include header row")
|
||||||
listCmd.Flags().VarP(&format, "format", "o", "output format (table|tsv|csv|markdown|html)")
|
listCmd.Flags().VarP(&format, "format", "o", "output format (table|tsv|csv|markdown|html)")
|
||||||
|
listCmd.Flags().StringSliceP("glob", "g", nil, "Filter keys with glob pattern (repeatable)")
|
||||||
|
listCmd.Flags().String("glob-sep", "", fmt.Sprintf("Characters treated as separators for globbing (default %q)", defaultGlobSeparatorsDisplay()))
|
||||||
rootCmd.AddCommand(listCmd)
|
rootCmd.AddCommand(listCmd)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
testdata/del__glob__mixed__ok.ct
vendored
Normal file
10
testdata/del__glob__mixed__ok.ct
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
$ pda set foo 1
|
||||||
|
$ pda set bar1 2
|
||||||
|
$ pda set bar2 3
|
||||||
|
$ pda del foo --glob bar* --force
|
||||||
|
$ pda get foo --> FAIL
|
||||||
|
Error: cannot get 'foo': Key not found
|
||||||
|
$ pda get bar1 --> FAIL
|
||||||
|
Error: cannot get 'bar1': Key not found
|
||||||
|
$ pda get bar2 --> FAIL
|
||||||
|
Error: cannot get 'bar2': Key not found
|
||||||
14
testdata/help__del__ok.ct
vendored
14
testdata/help__del__ok.ct
vendored
|
|
@ -9,9 +9,10 @@ Aliases:
|
||||||
del, delete, rm, remove
|
del, delete, rm, remove
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-f, --force Force delete without confirmation
|
-f, --force Force delete without confirmation
|
||||||
-g, --glob Treat KEY arguments as glob patterns
|
-g, --glob strings Delete keys matching glob pattern (repeatable)
|
||||||
-h, --help help for del
|
--glob-sep string Characters treated as separators for globbing (default "/-_.@: ")
|
||||||
|
-h, --help help for del
|
||||||
Delete one or more keys. Optionally specify a db.
|
Delete one or more keys. Optionally specify a db.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -21,6 +22,7 @@ Aliases:
|
||||||
del, delete, rm, remove
|
del, delete, rm, remove
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-f, --force Force delete without confirmation
|
-f, --force Force delete without confirmation
|
||||||
-g, --glob Treat KEY arguments as glob patterns
|
-g, --glob strings Delete keys matching glob pattern (repeatable)
|
||||||
-h, --help help for del
|
--glob-sep string Characters treated as separators for globbing (default "/-_.@: ")
|
||||||
|
-h, --help help for del
|
||||||
|
|
|
||||||
36
testdata/help__list__ok.ct
vendored
36
testdata/help__list__ok.ct
vendored
|
|
@ -9,14 +9,16 @@ Aliases:
|
||||||
list, ls
|
list, ls
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-b, --binary include binary data in text output
|
-b, --binary include binary data in text output
|
||||||
-o, --format format output format (table|tsv|csv|markdown|html) (default table)
|
-o, --format format output format (table|tsv|csv|markdown|html) (default table)
|
||||||
--header include header row
|
-g, --glob strings Filter keys with glob pattern (repeatable)
|
||||||
-h, --help help for list
|
--glob-sep string Characters treated as separators for globbing (default "/-_.@: ")
|
||||||
--no-keys suppress the key column
|
--header include header row
|
||||||
--no-values suppress the value column
|
-h, --help help for list
|
||||||
-S, --secret display values marked as secret
|
--no-keys suppress the key column
|
||||||
-t, --ttl append a TTL column when entries expire
|
--no-values suppress the value column
|
||||||
|
-S, --secret display values marked as secret
|
||||||
|
-t, --ttl append a TTL column when entries expire
|
||||||
List the contents of a db.
|
List the contents of a db.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -26,11 +28,13 @@ Aliases:
|
||||||
list, ls
|
list, ls
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-b, --binary include binary data in text output
|
-b, --binary include binary data in text output
|
||||||
-o, --format format output format (table|tsv|csv|markdown|html) (default table)
|
-o, --format format output format (table|tsv|csv|markdown|html) (default table)
|
||||||
--header include header row
|
-g, --glob strings Filter keys with glob pattern (repeatable)
|
||||||
-h, --help help for list
|
--glob-sep string Characters treated as separators for globbing (default "/-_.@: ")
|
||||||
--no-keys suppress the key column
|
--header include header row
|
||||||
--no-values suppress the value column
|
-h, --help help for list
|
||||||
-S, --secret display values marked as secret
|
--no-keys suppress the key column
|
||||||
-t, --ttl append a TTL column when entries expire
|
--no-values suppress the value column
|
||||||
|
-S, --secret display values marked as secret
|
||||||
|
-t, --ttl append a TTL column when entries expire
|
||||||
|
|
|
||||||
10
testdata/list__glob__ok.ct
vendored
Normal file
10
testdata/list__glob__ok.ct
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
$ pda set a1@lg 1
|
||||||
|
$ pda set a2@lg 2
|
||||||
|
$ pda set b1@lg 3
|
||||||
|
$ pda ls lg --glob a* --format tsv
|
||||||
|
a1 1
|
||||||
|
a2 2
|
||||||
|
$ pda ls lg --glob b* --format tsv
|
||||||
|
b1 3
|
||||||
|
$ pda ls lg --glob c* --> FAIL
|
||||||
|
Error: cannot ls '@lg': No matches for pattern
|
||||||
Loading…
Add table
Add a link
Reference in a new issue