Cleaned up the documentation and code a little.
This commit is contained in:
parent
69eca0eb16
commit
fc2fefd049
41
README.md
41
README.md
@ -1,25 +1,40 @@
|
||||
# arg - A simple command line argument parser
|
||||
This is a simple tool with a simple job: figure out the value of a single
|
||||
command line argument. Tools like `getopt` and `getopts` are much too
|
||||
complicated. It's much nicer to be able to write shell scripts like this:
|
||||
command line argument. Tools like `getopt` and `getopts` are unnecessarily
|
||||
complex and difficult to use, while also not supporting common use cases well.
|
||||
Parsing an argument in a shell script shouldn't require a tutorial or dozen
|
||||
lines of shell code!
|
||||
|
||||
To quote the `getopt` manpage:
|
||||
|
||||
> Each shellscript has to carry complex code to parse arguments halfway
|
||||
> correcty (like the example presented here). A better getopt-like tool would
|
||||
> move much of the complexity into the tool and keep the client shell scripts
|
||||
> simpler.
|
||||
|
||||
## Usage
|
||||
Simply run: `arg <flag> <extra args...>`. `<flag>` can be anything, like `-f`,
|
||||
`-flag`, `--flag`, or `flag`. If the flag occurs among the rest of the
|
||||
command line arguments, `arg` will print the flag's value (if any) and exit
|
||||
successfully, otherwise, it will fail (exit status 1). In a shell script, this
|
||||
would look like:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
dir=`arg --dir "$@" || arg -d "$@" || echo "$HOME/Downloads"`
|
||||
if ! server=`arg --server "$@" || arg -s "$@"`; then
|
||||
echo "No server provided :(" && exit 1
|
||||
fi
|
||||
if arg --verbose "$@" || arg -v "$@"; then
|
||||
echo "Downloading to $dir"
|
||||
fi
|
||||
cd $path && curl -O example.com/file.zip
|
||||
curl "$server/file.zip" > "$dir/file.zip"
|
||||
```
|
||||
|
||||
## Usage
|
||||
The following forms are accepted: `arg -f ...`, `arg -flag ...`, or `arg --flag
|
||||
...`. If `arg` finds the first given flag among the rest of the command line
|
||||
arguments, it will print the flag's value (if any) and exit successfully,
|
||||
otherwise, it will fail (exit status 1). The value may be of the form
|
||||
`--flag=value`, `--flag value`, `-f value`, or `-f=value`. Single-letter flags
|
||||
will match when grouped with other single letter flags, but will not have a
|
||||
value, i.e. `arg -b -abc foo` will exit successfully without printing anything.
|
||||
The flag's value may be of the form `<flag>=<value>` or `<flag> <value>`.
|
||||
Single-letter flags will also match when grouped with other single letter
|
||||
flags, but will not have a value (e.g. `arg -b -abc foo` will succeed without
|
||||
printing anything).
|
||||
|
||||
When using `arg` in a shell script, it is best to use quotes around `$@`, as in
|
||||
`arg --foo "$@"`, so arguments with spaces will parse properly, like
|
||||
`my_script.sh --flag "one two"`.
|
||||
`arg --foo "$@"`, so arguments with spaces will be forwarded correctly.
|
||||
|
40
arg.c
40
arg.c
@ -12,39 +12,43 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define EXIT_SUCCESS 0
|
||||
#define EXIT_NO_MATCH 1
|
||||
#define EXIT_BAD_USAGE -1
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *flag = argv[1];
|
||||
if (!flag || flag[0] != '-') {
|
||||
fprintf(stderr, "Usage: arg [-<f> | --<flag>] ...\n");
|
||||
return 1;
|
||||
if (!flag) {
|
||||
fprintf(stderr, "Usage: arg <flag> ...\n");
|
||||
return EXIT_BAD_USAGE;
|
||||
}
|
||||
size_t n = strlen(flag);
|
||||
size_t flaglen = strlen(flag);
|
||||
// Look for: --flag=val, --flag val, --flag, -f=val, -f val, or -f
|
||||
for (int i = 2; i < argc; i++) {
|
||||
char *arg = argv[i];
|
||||
if (strncmp(arg, flag, n) != 0) continue;
|
||||
if (arg[n] == '\0') { // --flag
|
||||
if (argv[i+1] && argv[i+1][0] != '-')
|
||||
puts(argv[i+1]); // value of flag, if any
|
||||
return 0;
|
||||
} else if (arg[n] == '=') { // --flag=...
|
||||
puts(&arg[n+1]);
|
||||
return 0;
|
||||
if (strncmp(arg, flag, flaglen) != 0) continue;
|
||||
if (arg[flaglen] == '\0') { // --flag ...
|
||||
if (argv[i+1] && argv[i+1][0] != '-') // --flag <value>
|
||||
puts(argv[i+1]); // value of the flag
|
||||
return EXIT_SUCCESS;
|
||||
} else if (arg[flaglen] == '=') { // --flag=...
|
||||
puts(&arg[flaglen+1]);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
// If flag is single-character, e.g. -f, look for it among other single
|
||||
// character flags like -xfy, in which case the next argument will not
|
||||
// be considered its value.
|
||||
if (n == 2) {
|
||||
// If flag is single-character without a value, e.g. -f, look for it
|
||||
// among other single character flags like -xfy.
|
||||
if (flag[0] == '-' && flaglen == 2) {
|
||||
for (int i = 2; i < argc; i++) {
|
||||
char *arg = argv[i];
|
||||
if (arg[0] != '-' || arg[1] == '-')
|
||||
continue; // skip foo and --foo
|
||||
for (char *c = &arg[1]; *c; c++) {
|
||||
if (*c == flag[1]) return 0;
|
||||
if (*c == flag[1])
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
return EXIT_NO_MATCH;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user