I was looking through the /etc/bash_completion script found in some Debian packages. I was interested in using the code that looks through a specific directory (/etc/bash_completion.d/ by default) and sources every file in that directory.
Unfortunately, trying to run the script causes errors under the Mac OS X version of bash. The lines in question are:
for i in $BASH_COMPLETION_DIR/*; do
[[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|.rpm*) ]] &&
[ \( -f $i -o -h $i \) -a -r $i ] && . $i
done
Specifically, my version of bash (3.2.17) chokes on the @() construction. I get that the point of that first test is to make sure we don’t source any editor swap files or backups, etc. I’d like to understand exactly what that @() syntax does, and, if possible how to get something similar (and similarly elegant) running on my ancient copy of bash. Can anyone offer insight?
It’s just an extension to the shell comparison which is equivalent to the
grep“or” operator(|).Depending on your bash version, it may not be available or you may have to set
extglobwith theshoptbuilt-in. See the following session transcript:pax@daemonspawn> $ bash --version GNU bash, version 3.2.48(21)-release (i686-pc-cygwin) Copyright (C) 2007 Free Software Foundation, Inc. pax@daemonspawn> echo @(*~|*.pl) bash: syntax error near unexpected token '(' pax@daemonspawn> shopt extglob extglob off pax@daemonspawn> shopt -s extglob pax@daemonspawn> echo @(*~|*.pl) qq.pl qq.sh~ xx.pl pax@daemonspawn>That allows the following to work:
?(pattern-list) Matches zero or one occurrence of the given patterns *(pattern-list) Matches zero or more occurrences of the given patterns +(pattern-list) Matches one or more occurrences of the given patterns @(pattern-list) Matches one of the given patterns !(pattern-list) Matches anything except one of the given patternsIf you can’t get it working with
shopt, you can generate a similar effect with older methods such as:#!/bin/bash for i in $BASH_COMPLETION_DIR/*; do # Ignore VIM, backup, swp, files with all #'s and install package files. # I think that's the right meaning for the '\#*\#' string. # I don't know for sure what it's meant to match otherwise. echo $i | egrep '~$|\.bak$|\.swp$|^#*#$|\.dpkg|\.rpm' >/dev/null 2>&1 if [[ $? == 0 ]] ; then . $i fi doneAlternatively, if there’s multiple complex determinations that will decide whether you want it sourced, you can use a
doitvariable that’s initially set totrue, and set it tofalseif any of those conditions trigger. For example, the following script qq.sh:#!/bin/bash for i in * ; do doit=1 # Ignore VIM backups. echo $i | egrep '~$' >/dev/null 2>&1 if [[ $? -eq 0 ]] ; then doit=0 fi # Ignore Perl files. echo $i | egrep '\.pl$' >/dev/null 2>&1 if [[ $? -eq 0 ]] ; then doit=0 fi if [[ ${doit} -eq 1 ]] ; then echo Processing $i else echo Ignoring $i fi donedid this in my home directory:
Processing Makefile Processing binmath.c : : : : : Processing qq.in Ignoring qq.pl --+ Processing qq.sh | Ignoring qq.sh~ --+--- see? Processing qqin | : : : : : | Ignoring xx.pl --+