<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div style="" class="markdown-here-wrapper"
data-md-url="Thunderbird">
<p style="margin: 1.2em 0px ! important;">Hello all,</p>
<p style="margin: 1.2em 0px ! important;">I’ve recently written a
few Bash functions to help me define completions for some
personal scripts and commands, and I would appreciate your
opinion and suggestions for improvements. As no explanation
beats a good example, here is a small example of a completion
description with these functions (they all start with ‘C.’ to
facilitate reading and to avoid name clashes) :</p>
<pre style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block ! important;">source /usr/share/bash/bashcomps.shl # I had some qualms about putting a shell script in /usr/lib
function C.example() {
C.alt C.describing "a color" C.wordOf 5 red green blue black white \
C.describing "a file" C.fileIn -f . \
C.normal "$@"
C.alt C.describing "invert colors" C.flagOf 2 -I --invert \
C.normal "$@"
}
C.defcomp 'C.repeat C.example' example
</code></pre>
<p style="margin: 1.2em 0px ! important;">These few lines define a
completion function that completes a color then a file, or a
single flag, repeated ad infinitum. This is of course a
contrived example, but the approach can trivially be extended to
more useful completions.</p>
<p style="margin: 1.2em 0px ! important;">The library predefines a
small number of combinators, that can then be used to construct
all sorts of complex structures within the arguments. For
example, you can complete a URL by simply combining a protocol,
a <code style="font-size: 0.85em; font-family:
Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
rgb(234, 234, 234); background-color: rgb(248, 248, 248);
border-radius: 3px; display: inline;">://</code>, a hostname,
a <code style="font-size: 0.85em; font-family:
Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
rgb(234, 234, 234); background-color: rgb(248, 248, 248);
border-radius: 3px; display: inline;">/</code> and a path. In
other words :</p>
<pre style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block ! important;">function C.url() {
local prevSuf="$COMP_SUFFIX"
C.suffixed '://' C.wordOf 3 http https ftp \
C.suffixed '/' C.hostname \
C.suffixed "$prevSuf" C.any "$@"
}
</code></pre>
<p style="margin: 1.2em 0px ! important;">After that, completing a
URL as part of an option becomes a piece of cake : </p>
<pre style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block ! important;">function C.urlOpt() {
C.optOf 2 --url -U C.url "$@"
}
</code></pre>
<p style="margin: 1.2em 0px ! important;">Of course, it is still
possible to write your own generation functions and plug them
into a parsing chain using a function called <code
style="font-size: 0.85em; font-family:
Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
rgb(234, 234, 234); background-color: rgb(248, 248, 248);
border-radius: 3px; display: inline;">C.argument</code> (in
fact, all other helpers are defined in terms of <code
style="font-size: 0.85em; font-family:
Consolas,Inconsolata,Courier,monospace;margin: 0px 0.15em;
padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid
rgb(234, 234, 234); background-color: rgb(248, 248, 248);
border-radius: 3px; display: inline;">C.argument</code>) but I
won’t bore you with the details here, that’s what the man page
is for ;-)</p>
<p style="margin: 1.2em 0px ! important;">If you want to test it
for yourself, you can download it <a
href="https://pool.coiffier.net/packages/bashcomps.tgz">here</a>
(for a distribution-independent .tar.gz version), or directly as
a <a href="https://pool.coiffier.net/packages/bashcomps.deb">.deb</a>
or <a
href="https://pool.coiffier.net/packages/bashcomps.tar.xz">.tar.xz</a>
if your distro supports it.</p>
<p style="margin: 1.2em 0px ! important;">Anyway, thank you for
reading this far, and for any feedback or opinion you might have
:-)</p>
<p style="margin: 1.2em 0px ! important;">Yours indubitably,<br>
Marc Coiffier</p>
<div
title="MDH:SGVsbG8gYWxsLDxicj48YnI+SSd2ZSByZWNlbnRseSB3cml0dGVuIGEgZmV3IEJhc2ggZnVuY3Rpb25zIHRvIGhlbHAgbWUgZGVmaW5lIGNvbXBsZXRpb25zIGZvciBzb21lIHBlcnNvbmFsIHNjcmlw
dHMgYW5kIGNvbW1hbmRzLCBhbmQgSSB3b3VsZCBhcHByZWNpYXRlIHlvdXIgb3BpbmlvbiBhbmQg
c3VnZ2VzdGlvbnMgZm9yIGltcHJvdmVtZW50cy4gQXMgbm8gZXhwbGFuYXRpb24gYmVhdHMgYSBn
b29kIGV4YW1wbGUsIGhlcmUgaXMgYSBzbWFsbCBleGFtcGxlIG9mIGEgY29tcGxldGlvbiBkZXNj
cmlwdGlvbiB3aXRoIHRoZXNlIGZ1bmN0aW9ucyAodGhleSBhbGwgc3RhcnQgd2l0aCAnQy4nIHRv
IGZhY2lsaXRhdGUgcmVhZGluZyBhbmQgdG8gYXZvaWQgbmFtZSBjbGFzaGVzKSA6PGJyPjxicj7C
oMKgwqAgc291cmNlIC91c3Ivc2hhcmUvYmFzaC9iYXNoY29tcHMuc2hsICMgSSBoYWQgc29tZSBx
dWFsbXMgYWJvdXQgcHV0dGluZyBhIHNoZWxsIHNjcmlwdCBpbiAvdXNyL2xpYjxicj7CoMKgwqAg
PGJyPsKgwqDCoCBmdW5jdGlvbiBDLmV4YW1wbGUoKSB7PGJyPsKgwqDCoMKgwqDCoMKgIEMuYWx0
IEMuZGVzY3JpYmluZyAiYSBjb2xvciIgQy53b3JkT2YgNSByZWQgZ3JlZW4gYmx1ZSBibGFjayB3
aGl0ZSBcPGJyPsKgwqDCoMKgwqDCoMKgIMKgIMKgIMKgIMKgIEMuZGVzY3JpYmluZyAiYSBmaWxl
IiBDLmZpbGVJbiAtZiAuIFw8YnI+wqDCoMKgwqDCoMKgwqAgwqAgwqAgwqDCoMKgIEMubm9ybWFs
ICIkQCI8YnI+wqAgwqAgwqAgwqAgQy5hbHQgQy5kZXNjcmliaW5nICJpbnZlcnQgY29sb3JzIiBD
LmZsYWdPZiAyIC1JIC0taW52ZXJ0IFw8YnI+wqDCoMKgwqDCoMKgIMKgIMKgIMKgIMKgwqAgQy5u
b3JtYWwgIiRAIjxicj7CoMKgwqDCoCB9PGJyPsKgwqDCoCBDLmRlZmNvbXAgJ0MucmVwZWF0IEMu
ZXhhbXBsZScgZXhhbXBsZTxicj48YnI+VGhlc2UgZmV3IGxpbmVzIGRlZmluZSBhIGNvbXBsZXRp
b24gZnVuY3Rpb24gdGhhdCBjb21wbGV0ZXMgYSBjb2xvciB0aGVuIGEgZmlsZSwgb3IgYSBzaW5n
bGUgZmxhZywgcmVwZWF0ZWQgYWQgaW5maW5pdHVtLiBUaGlzIGlzIG9mIGNvdXJzZSBhIGNvbnRy
aXZlZCBleGFtcGxlLCBidXQgdGhlIGFwcHJvYWNoIGNhbiB0cml2aWFsbHkgYmUgZXh0ZW5kZWQg
dG8gbW9yZSB1c2VmdWwgY29tcGxldGlvbnMuPGJyPjxicj5UaGUgbGlicmFyeSBwcmVkZWZpbmVz
IGEgc21hbGwgbnVtYmVyIG9mIGNvbWJpbmF0b3JzLCB0aGF0IGNhbiB0aGVuIGJlIHVzZWQgdG8g
Y29uc3RydWN0IGFsbCBzb3J0cyBvZiBjb21wbGV4IHN0cnVjdHVyZXMgd2l0aGluIHRoZSBhcmd1
bWVudHMuIEZvciBleGFtcGxlLCB5b3UgY2FuIGNvbXBsZXRlIGEgVVJMIGJ5IHNpbXBseSBjb21i
aW5pbmcgYSBwcm90b2NvbCwgYSBgOi8vYCwgYSBob3N0bmFtZSwgYSBgL2AgYW5kIGEgcGF0aC4g
SW4gb3RoZXIgd29yZHMgOjxicj48YnI+wqDCoMKgIGZ1bmN0aW9uIEMudXJsKCkgezxicj7CoMKg
wqDCoMKgwqDCoCBsb2NhbCBwcmV2U3VmPSIkQ09NUF9TVUZGSVgiPGJyPsKgwqDCoMKgwqDCoMKg
IEMuc3VmZml4ZWQgJzovLycgQy53b3JkT2YgMyBodHRwIGh0dHBzIGZ0cCBcPGJyPsKgwqDCoMKg
wqDCoMKgwqDCoMKgIEMuc3VmZml4ZWQgJy8nIEMuaG9zdG5hbWUgXDxicj7CoMKgwqDCoMKgwqDC
oMKgwqDCoCBDLnN1ZmZpeGVkICIkcHJldlN1ZiIgQy5hbnkgIiRAIjxicj7CoMKgwqAgfTxicj48
YnI+QWZ0ZXIgdGhhdCwgY29tcGxldGluZyBhIFVSTCBhcyBwYXJ0IG9mIGFuIG9wdGlvbiBiZWNv
bWVzIGEgcGllY2Ugb2YgY2FrZSA6IDxicj48YnI+wqDCoMKgIGZ1bmN0aW9uIEMudXJsT3B0KCkg
ezxicj7CoMKgwqAgwqAgwqAgQy5vcHRPZiAyIC0tdXJsIC1VIEMudXJsICIkQCI8YnI+wqDCoMKg
IH08YnI+PGJyPk9mIGNvdXJzZSwgaXQgaXMgc3RpbGwgcG9zc2libGUgdG8gd3JpdGUgeW91ciBv
d24gZ2VuZXJhdGlvbiBmdW5jdGlvbnMgYW5kIHBsdWcgdGhlbSBpbnRvIGEgcGFyc2luZyBjaGFp
biB1c2luZyBhIGZ1bmN0aW9uIGNhbGxlZCBgQy5hcmd1bWVudGAgKGluIGZhY3QsIGFsbCBvdGhl
ciBoZWxwZXJzIGFyZSBkZWZpbmVkIGluIHRlcm1zIG9mIGBDLmFyZ3VtZW50YCkgYnV0IEkgd29u
J3QgYm9yZSB5b3Ugd2l0aCB0aGUgZGV0YWlscyBoZXJlLCB0aGF0J3Mgd2hhdCB0aGUgbWFuIHBh
Z2UgaXMgZm9yIDstKTxicj48YnI+SWYgeW91IHdhbnQgdG8gdGVzdCBpdCBmb3IgeW91cnNlbGYs
IHlvdSBjYW4gZG93bmxvYWQgaXQgW2hlcmVdKGh0dHBzOi8vcG9vbC5jb2lmZmllci5uZXQvcGFj
a2FnZXMvYmFzaGNvbXBzLnRneikgKGZvciBhIGRpc3RyaWJ1dGlvbi1pbmRlcGVuZGVudCAudGFy
Lmd6IHZlcnNpb24pLCBvciBkaXJlY3RseSBhcyBhIFsuZGViXShodHRwczovL3Bvb2wuY29pZmZp
ZXIubmV0L3BhY2thZ2VzL2Jhc2hjb21wcy5kZWIpIG9yIFsudGFyLnh6XShodHRwczovL3Bvb2wu
Y29pZmZpZXIubmV0L3BhY2thZ2VzL2Jhc2hjb21wcy50YXIueHopIGlmIHlvdXIgZGlzdHJvIHN1
cHBvcnRzIGl0Ljxicj48YnI+QW55d2F5LCB0aGFuayB5b3UgZm9yIHJlYWRpbmcgdGhpcyBmYXIs
IGFuZCBmb3IgYW55IGZlZWRiYWNrIG9yIG9waW5pb24geW91IG1pZ2h0IGhhdmUgOi0pPGJyPjxi
cj5Zb3VycyBpbmR1Yml0YWJseSw8YnI+TWFyYyBDb2lmZmllcjxicj48YnI+"
style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0;"></div>
</div>
</body>
</html>