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
|
# arg - A simple command line argument parser
|
||||||
This is a simple tool with a simple job: figure out the value of a single
|
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
|
command line argument. Tools like `getopt` and `getopts` are unnecessarily
|
||||||
complicated. It's much nicer to be able to write shell scripts like this:
|
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
|
#!/bin/sh
|
||||||
dir=`arg --dir "$@" || arg -d "$@" || echo "$HOME/Downloads"`
|
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
|
if arg --verbose "$@" || arg -v "$@"; then
|
||||||
echo "Downloading to $dir"
|
echo "Downloading to $dir"
|
||||||
fi
|
fi
|
||||||
cd $path && curl -O example.com/file.zip
|
curl "$server/file.zip" > "$dir/file.zip"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
The flag's value may be of the form `<flag>=<value>` or `<flag> <value>`.
|
||||||
The following forms are accepted: `arg -f ...`, `arg -flag ...`, or `arg --flag
|
Single-letter flags will also match when grouped with other single letter
|
||||||
...`. If `arg` finds the first given flag among the rest of the command line
|
flags, but will not have a value (e.g. `arg -b -abc foo` will succeed without
|
||||||
arguments, it will print the flag's value (if any) and exit successfully,
|
printing anything).
|
||||||
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.
|
|
||||||
When using `arg` in a shell script, it is best to use quotes around `$@`, as in
|
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
|
`arg --foo "$@"`, so arguments with spaces will be forwarded correctly.
|
||||||
`my_script.sh --flag "one two"`.
|
|
||||||
|
40
arg.c
40
arg.c
@ -12,39 +12,43 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#define EXIT_SUCCESS 0
|
||||||
|
#define EXIT_NO_MATCH 1
|
||||||
|
#define EXIT_BAD_USAGE -1
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
char *flag = argv[1];
|
char *flag = argv[1];
|
||||||
if (!flag || flag[0] != '-') {
|
if (!flag) {
|
||||||
fprintf(stderr, "Usage: arg [-<f> | --<flag>] ...\n");
|
fprintf(stderr, "Usage: arg <flag> ...\n");
|
||||||
return 1;
|
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
|
// Look for: --flag=val, --flag val, --flag, -f=val, -f val, or -f
|
||||||
for (int i = 2; i < argc; i++) {
|
for (int i = 2; i < argc; i++) {
|
||||||
char *arg = argv[i];
|
char *arg = argv[i];
|
||||||
if (strncmp(arg, flag, n) != 0) continue;
|
if (strncmp(arg, flag, flaglen) != 0) continue;
|
||||||
if (arg[n] == '\0') { // --flag
|
if (arg[flaglen] == '\0') { // --flag ...
|
||||||
if (argv[i+1] && argv[i+1][0] != '-')
|
if (argv[i+1] && argv[i+1][0] != '-') // --flag <value>
|
||||||
puts(argv[i+1]); // value of flag, if any
|
puts(argv[i+1]); // value of the flag
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
} else if (arg[n] == '=') { // --flag=...
|
} else if (arg[flaglen] == '=') { // --flag=...
|
||||||
puts(&arg[n+1]);
|
puts(&arg[flaglen+1]);
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If flag is single-character, e.g. -f, look for it among other single
|
// If flag is single-character without a value, e.g. -f, look for it
|
||||||
// character flags like -xfy, in which case the next argument will not
|
// among other single character flags like -xfy.
|
||||||
// be considered its value.
|
if (flag[0] == '-' && flaglen == 2) {
|
||||||
if (n == 2) {
|
|
||||||
for (int i = 2; i < argc; i++) {
|
for (int i = 2; i < argc; i++) {
|
||||||
char *arg = argv[i];
|
char *arg = argv[i];
|
||||||
if (arg[0] != '-' || arg[1] == '-')
|
if (arg[0] != '-' || arg[1] == '-')
|
||||||
continue; // skip foo and --foo
|
continue; // skip foo and --foo
|
||||||
for (char *c = &arg[1]; *c; c++) {
|
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