Repository: borgbackup/borg

Issue:
Unable to parse pattern/command
Please see output...

```
[root@chopin ~]# borg create --pattern /home/dave/Projects/borgtest /backup/ninja-test
Traceback (most recent call last):
  File "/usr/lib64/python3.4/site-packages/borg/patterns.py", line 372, in parse_inclexcl_command
    cmd = cmd_prefix_map[cmd_line_str[0]]
KeyError: '/'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python-exec/python3.4/borg", line 11, in <module>
    load_entry_point('borgbackup==1.1.4', 'console_scripts', 'borg')()
  File "/usr/lib64/python3.4/site-packages/borg/archiver.py", line 4164, in main
    args = archiver.get_args(sys.argv, os.environ.get('SSH_ORIGINAL_COMMAND'))
  File "/usr/lib64/python3.4/site-packages/borg/archiver.py", line 4005, in get_args
    result = self.parse_args(argv[1:])
  File "/usr/lib64/python3.4/site-packages/borg/archiver.py", line 4027, in parse_args
    args = parser.parse_args(args or ['-h'])
  File "/usr/lib64/python3.4/argparse.py", line 1728, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/usr/lib64/python3.4/argparse.py", line 1760, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/usr/lib64/python3.4/argparse.py", line 1948, in _parse_known_args
    positionals_end_index = consume_positionals(start_index)
  File "/usr/lib64/python3.4/argparse.py", line 1925, in consume_positionals
    take_action(action, args)
  File "/usr/lib64/python3.4/argparse.py", line 1834, in take_action
    action(self, namespace, argument_values, option_string)
  File "/usr/lib64/python3.4/argparse.py", line 1129, in __call__
    subnamespace, arg_strings = parser.parse_known_args(arg_strings, None)
  File "/usr/lib64/python3.4/argparse.py", line 1760, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/usr/lib64/python3.4/argparse.py", line 1966, in _parse_known_args
    start_index = consume_optional(start_index)
  File "/usr/lib64/python3.4/argparse.py", line 1906, in consume_optional
    take_action(action, args, option_string)
  File "/usr/lib64/python3.4/argparse.py", line 1834, in take_action
    action(self, namespace, argument_values, option_string)
  File "/usr/lib64/python3.4/site-packages/borg/patterns.py", line 44, in __call__
    parse_patternfile_line(values[0], args.paths, args.patterns, ShellPattern)
  File "/usr/lib64/python3.4/site-packages/borg/patterns.py", line 16, in parse_patternfile_line
    ie_command = parse_inclexcl_command(line, fallback=fallback)
  File "/usr/lib64/python3.4/site-packages/borg/patterns.py", line 380, in parse_inclexcl_command
    raise argparse.ArgumentTypeError("Unable to parse pattern/command: {}".format(cmd_line_str))
argparse.ArgumentTypeError: Unable to parse pattern/command: /home/dave/Projects/borgtest
```


Discussion:
So, what is in your borgtest patterns file? Looks like it can't be parsed.
I don't have a patterns file, I was using --pattern PATTERN, where PATTERN = /home/dave/Projects/borgtest.
ah, right, but that is not a valid pattern.

https://borgbackup.readthedocs.io/en/stable/usage/help.html see the part with the red marks at the borders.

borg needs at least one root ("starting point"), plus includes/excludes as desired.
maybe we could make it blow up less spectacularly (no traceback) for such simple errors.
Yes, I was just going to say the error could have been more helpful.  And after re-reading the documentation, I still don't see that it says you have to specify a root path for --pattern to work.
One way to give borg roots is like this:
```
borg create repo::archive root1 root2 ...
```
The other way is to have everything in a patterns file (or patterns arguments).
Hmm.  As I understand it, adding a PATH still would not allow --pattern to work.  And it seems that this also does not work...
--pattern 'R/' --pattern '/home/dave/Projects/borgtest/'
That root does not apply to subsequent patterns on the command line.  Which seems to imply that the only patterns you can pass on the command line are roots, which must be plain paths, not patterns.  Am I missing something?

I understand that I can use --patterns-from, but I'm trying to work on a borg handler for backupninja that passes the include/exclude patterns as arguments.  The way backupninja handles rdiff-backup is to pass arguments something like this...
`rdiff-backup --exclude <pattern> --include <pattern> --include <pattern> --exclude '/*' / <destination>`
So, the source is the literal root, but the final exclude of '/*' eliminates everything that is not explicitly included.  The script, then, just deals with patterns from anywhere in the root.  That's what I'm trying to replicate with borg.  The --paterns-from example (red marks) has something like that, I'm just trying to find the command line argument way to get there.

Please let me know if I should take this to the mailing list instead.  Thanks.
You are still missing that you can't just give a path for --pattern, you also need + - ! in front of it, see the docs.
I see.  I assumed that --pattern implied '+' and --exclude implied '-', similar to rdiff-backup and rsync.  Instead, --pattern (in addition to --pattern-file) requires +/-/!; --exclude and --exclude-from cannot have any of +/-/!.  Do I have that correct?  Is it also correct that `--pattern '-fm:<pattern>'` is equivalent to `--exclude '<pattern>'`?

I'm sorry that I have so many questions.  It would help a lot if the documentation included a command line example for --pattern.

Thanks.
Don't make any assumptions based on "othertool does it like that".

And guess what, if --pattern implied "+", it would not be called --pattern, but --include.
It is called --pattern because you can use it to give one of the lines as seen in a --patterns-from file directly on the commandline, without using such a file.

The --patterns(-from) stuff is a new matching system (see docs: experimental), intended to be more powerful and flexible than the old matching system (which just had root-paths and excludes).

We have docs, read them (again). If there is something missing in the docs or hard to understand, find it out and do a PR, so you can help improving them.
I was just trying to explain why a new user with a background in other tools might need a little more hand-holding.  If that kind of feedback doesn't interest you, that's fine.  At this time, it wouldn't be wise for me to do a PR, as I am still trying to figure it out how everything works.  I have moved my questions to the mailing list.  Thanks.

Resolving patch:
diff --git a/src/borg/patterns.py b/src/borg/patterns.py
--- a/src/borg/patterns.py
+++ b/src/borg/patterns.py
@@ -367,17 +367,18 @@ def parse_inclexcl_command(cmd_line_str, fallback=ShellPattern):
         'P': IECommand.PatternStyle,
         'p': IECommand.PatternStyle,
     }
-
-    try:
-        cmd = cmd_prefix_map[cmd_line_str[0]]
-
-        # remaining text on command-line following the command character
-        remainder_str = cmd_line_str[1:].lstrip()
-
-        if not remainder_str:
-            raise ValueError("Missing pattern/information!")
-    except (IndexError, KeyError, ValueError):
-        raise argparse.ArgumentTypeError("Unable to parse pattern/command: {}".format(cmd_line_str))
+    if not cmd_line_str:
+        raise argparse.ArgumentTypeError("A pattern/command must not be empty.")
+
+    cmd = cmd_prefix_map.get(cmd_line_str[0])
+    if cmd is None:
+        raise argparse.ArgumentTypeError("A pattern/command must start with any one of: %s" %
+                                         ', '.join(cmd_prefix_map))
+
+    # remaining text on command-line following the command character
+    remainder_str = cmd_line_str[1:].lstrip()
+    if not remainder_str:
+        raise argparse.ArgumentTypeError("A pattern/command must have a value part.")
 
     if cmd is IECommand.RootPath:
         # TODO: validate string?


Test patch: