Пакет bash-completion
не делает этого, он выполняет некоторые акробатические трюки для обработки обоих параметров командной строки и извлечения списка целей Makefile
, но он не пытается генерировать совпадения, применяя подстановочные знаки или иным образом обрабатывая любые правила шаблона .
Однако это можно сделать, вот простая версия с некоторыми оговорками.
function _mkcache() {
local _file="$1"
# add "-r" to omit defaults (60+ rules)
${MAKE:-make} ${_file:+-f "$_file"} -qp 2>/dev/null |
gawk '/^# *Make data base/,/^# *Finished Make data base/{
if (/^# Not a target/) { getline; next }
## handle "target:..."
if (match($0,/^([^.#% ][^:%=]+) *:($|[^=])(.*)/,bits)) {
#if (bits[3]=="") next # OPT: skip phony
printf("%s\n",bits[1])
}
## handle "%.x [...]: %.y [| x]", split into distinct targets/prereqs
else if (match($0,/^([^:]*%[^:]*) *(::?) *(.*%.*) *(\| *(.*))?/,bits)) {
#if (bits[3]=="%") next # OPT: skip wildcard ones
nb1=split(bits[1],bb1)
nb3=split(bits[3],bb3)
for (nn=1; nn<=nb1; nn++)
for (mm=1; mm<=nb3; mm++)
printf("%s : %s\n",bb1[nn],bb3[mm])
}
## handle fixed (no %) deps
else if (match($0,/^([^:]*%[^:]*) *(::?) *([^%]*)$/,bits)) {
if (bits[3]=="") next # phony
printf("%s : %s\n",bits[1],bits[3])
}
## handle old form ".c.o:" rewrite to new form "%.o: %.c"
else if (match($0,/^\.([^.]+)\.([^.]+): *(.*)/,bits)) {
printf("%%.%s : %%.%s\n", bits[2],bits[1])
}
}' > ".${_file:-Makefile}.targets"
}
function _bc_make() {
local ctok=${COMP_WORDS[COMP_CWORD]} # curr token
local ptok=${COMP_WORDS[COMP_CWORD-1]} # prev token
local -a mkrule maybe
local try rr lhs rhs rdir pat makefile=Makefile
## check we're not doing any make options
[[ ${ctok:0:1} != "-" && ! $ptok =~ ^-[fCIjloW] ]] && {
COMPREPLY=()
[[ "$makefile" -nt.${makefile}.targets ]] &&
_mkcache "$makefile"
mapfile -t mkrule < ".${makefile}.targets"
# mkrule+=( "%.o : %.c" ) # stuff in extra rules
for rr in "${mkrule[@]}"; do
IFS=": " read lhs rhs <<< $rr
## special "archive(member):"
[[ "$lhs" =~ ^(.*)?\((.+)\) ]] && {
continue # not handled
}
## handle simple targets
[[ "$rhs" == "" ]] && {
COMPREPLY+=( $(compgen -W "$lhs" -- "$ctok" ) )
continue
}
## rules with a path, like "% : RCS/%,v"
rdir=""
[[ "$rhs" == */* ]] && rdir="${rhs/%\/*/}/"
rhs=${rhs/#*\//}
## expand (glob) that matches RHS
## if current token already ends in a "." strip it
## match by replacing "%" stem with "*"
[[ $ctok == *. ]] && try="${rdir}${rhs/\%./$ctok*}" \
|| try="${rdir}${rhs/\%/$ctok*}"
maybe=( $(compgen -G "$try") ) # try must be quoted
## maybe[] is an array of filenames from expanded prereq globs
(( ${#maybe[*]} )) && {
[[ "$rhs" =~ % ]] && {
## promote rhs glob to a regex: % -> (.*)
rhs="${rhs/./\\.}"
pat="${rdir}${rhs/\%/(.*)}"
## use regex to extract stem from RHS, sub "%" on LHS
for nn in "${maybe[@]}"; do
[[ $nn =~ $pat ]] && {
COMPREPLY+=( "${lhs/\%/${BASH_REMATCH[1]}}" )
}
done
} || {
# fixed prereqs (no % on RHS)
COMPREPLY+=( "${lhs/\%/$ctok}" )
}
}
done
return
}
COMPREPLY=() #default
}
complete -F _bc_make ${MAKE:-make}
Есть две части: функция _mkcache
извлекает все правила и цели из Makefile
и кэширует их. Он также выполняет некоторую обработку, поэтому правила упрощаются до одной формы " target : pre-req
" в этом кэше.
Затем функция завершения _bc_make
берет токен, на котором вы пытаетесь выполнить завершение, и пытается сопоставить его с целями, а также использует правила шаблона для расширения глобуса на основе предварительных -реквизитов и слова для завершения. Если найдено одно или несколько совпадений, он создает список целей на основе правил шаблона.
Предполагается GNU make
. Он должен корректно обрабатывать:
.c.o
→%.o : %.c
RCS/
)-r
к make
, если хотите)Предостережения и не поддерживается:
make
VPATH
илиvpath
.SUFFIXES
make -C dir
make
расширение опций TERMCAP
, например)Makefile
Некоторые из вышеперечисленных могут быть добавлены относительно просто, другие, такие как обработка архива, не так просты.
Это ошибка в bash. Вы должны отправить отчет об ошибке по адресу bug -bash@gnu.org.
Более впечатляющий пример --, как вы можете видеть, это комбинация "скрипт из стандартного ввода", "стандартный ввод в качестве файла с возможностью поиска" и "heredoc из фоновой команды", которая запускает его, вложенных здесьдокументов недостаточно или необходимо:
$ cat a.sh
true <<EOF &
#ABC
EOF
seq 1 3
true | true
#1234567
$ bash <a.sh
1
2
3
bash: line 5: rue: command not found
1
2
3
bash: line 9: rue: command not found
1
...
<same nonsense repeated hundred of times with increasing line numbers>
Вы можете обойти это с помощью «бесполезного» использования cat на внешних вкладках bash (, опущенных в вашем примере, поскольку этот сайт их искажает):
cat <<-EOS1 | bash -s
bash -s <<EOS2 &
#ABC
EOS2
echo "" | wc
#1234567
EOS1
Поскольку скрипт будет считываться из канала, это убедит bash читать его байт -за -байт (системным вызовом для каждого байта )вместо того, чтобы пытаться срезать углы, ища назад и вперед на вход ;-)