Странное поведение вложенного документа

Пакет 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расширение опций
  • патологический мусор в среде, который может вызвать проблемы с разбором Makefile (TERMCAP, например)
  • Makefiles с именами, отличными отMakefile

Некоторые из вышеперечисленных могут быть добавлены относительно просто, другие, такие как обработка архива, не так просты.

4
13.09.2020, 20:49
1 ответ

Это ошибка в 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 читать его байт -за -байт (системным вызовом для каждого байта )вместо того, чтобы пытаться срезать углы, ища назад и вперед на вход ;-)

4
18.03.2021, 23:05

Теги

Похожие вопросы