Каковы правила для допустимых идентификаторов (например, функций, переменных и т. Д.) В Bash?

Каковы правила синтаксиса для идентификаторов, особенно имен функций и переменных, в Bash?

Я написал сценарий Bash и протестировал его в различных версиях Bash для Ubuntu, Debian, Red Hat 5 и 6 и даже старой версии Solaris 8. Сценарий работает хорошо, поэтому он отправлен.

Тем не менее, когда пользователь пробовал его на компьютерах SUSE, он выдавал ошибку "неверный идентификатор". К счастью, мое предположение, что в имени функции был неверный символ, оказалось верным. Дефисы все испортили.

Тот факт, что скрипт, который был хотя бы несколько протестирован, будет иметь совершенно другое поведение в другом Bash или дистрибутиве, сбивал с толку. Как я могу избежать этого?

6 ответов

Решение
   Shell Function Definitions
       ...
       name () compound-command [redirection]
       function name [()] compound-command [redirection]

name определяется в другом месте:

       name   A  word  consisting  only  of alphanumeric characters and under‐
              scores, and beginning with an alphabetic character or an  under‐
              score.  Also referred to as an identifier.

Таким образом, дефисы не действительны. И все же в моей системе они работают...

$ bash --version
GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)

Вопрос был о "правилах", на которые отвечали двумя различными способами, каждый из которых в некотором смысле правильный, в зависимости от того, что вы хотите назвать "правилами". Просто чтобы подчеркнуть точку зрения @ rici о том, что вы можете указать любой символ в имени функции, я написал небольшой скрипт bash, чтобы попытаться проверить каждый возможный (0-255) символ как имя функции, а также второй символ имя функции:

#!/bin/bash
ASCII=( nul soh stx etx eot enq ack bel bs tab nl vt np cr so si dle \
            dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us sp )

for((i=33; i < 127; ++i)); do
    printf -v Hex "%x" $i

    printf -v Chr "\x$Hex"
    ASCII[$i]="$Chr"
done
ASCII[127]=del
for((i=128; i < 256; ++i)); do
    ASCII[$i]=$(printf "0X%x" $i)
done

# ASCII table is now defined

function Test(){
    Illegal=""
    for((i=1; i <= 255; ++i)); do
        Name="$(printf \\$(printf '%03o' $i))"
        eval "function $1$Name(){ return 0; }; $1$Name ;" 2>/dev/null
        if [[ $? -ne 0 ]]; then
            Illegal+=" ${ASCII[$i]}"
            #        echo Illegal: "${ASCII[$i]}"
        fi
    done
    printf "Illegal: %s\n" "$Illegal"
}
echo "$BASH_VERSION"
Test
Test "x"

# can we really do funky crap like this?
function [}{(){
   echo "Let me take you to, funkytown!"
}
[}{    # why yes, we can!
# though editor auto-indent modes may punish us

Я на самом деле пропускаю NUL (0x00), так как это один символ bash может возражать против поиска во входном потоке. Вывод из этого скрипта был:

4.4.0(1)-release
Illegal:  soh tab nl sp ! " # $ % & ' ( ) * 0 1 2 3 4 5 6 7 8 9 ; < > \ ` { | } ~ del
Illegal:  soh " $ & ' ( ) ; < > [ \ ` | del
Let me take you to, funkytown!

Обратите внимание, что bash с радостью позволяет мне назвать мою функцию "[}{". Возможно, мой код недостаточно строг, чтобы предоставить точные правила для легальности на практике, но он должен дать представление о том, какой способ злоупотребления возможен. Хотел бы я отметить этот ответ "Только для зрелой аудитории".

Идентификаторы команд и имена переменных имеют разные синтаксисы. Имя переменной ограничено буквенно-цифровыми символами и подчеркиванием, не начинающимся с цифры. С другой стороны, имя команды может быть практически любым, не содержащим метасимволов bash (и даже в этом случае они могут быть заключены в кавычки).

В bash имена функций могут быть именами команд, если они будут анализироваться как WORD без кавычек. (За исключением того, что по какой-то причине они не могут быть целыми числами.) Однако это расширение bash. Если на целевой машине используется какая-либо другая оболочка (например, тире), она может не работать, поскольку стандартная грамматика оболочки Posix допускает только "ИМЯ" в форме определения функции (а также запрещает использование зарезервированных слов).

Примечание. Самым большим исправлением здесь является то, что в имени функции никогда не допускается перевод строки .

Мой ответ:

  • Баш --posix: [a-zA-Z_][0-9a-zA-Z_]*
  • Баш 3.0-4.4:
  • Баш 5.0: [^#%0-9\0\9\10 "$&'();<>\`|][^\0\9\10 "$&'();<>\`|]*
    • \1 а также \x7f работает сейчас
  • Баш 5.1: [^#%\0\9\10 "$&'();<>\`|][^\0\9\10 "$&'();<>\`|]*
    • Числа могут быть на первом месте ?! Ага!
  • Любой bash 3-5: [^#%0-9\0\1\9\10 "$&'();<>\`|\x7f][^\0\1\9\10 "$&'();<>\`|\x7f]*
    • То же, что 3.0-4.4
  • Мое предложение (мнение): [^#%0-9\0-\f "$&'();<>\`|\x7f-\xff][^\0-\f "$&'();<>\`|\x7f-\xff]
    • Положительная версия: [!*+,-./:=?@A-Z\[\]^_a-z{}~][#%0-9!*+,-./:=?@A-Z\[\]^_a-z{}~]*

Моя версия теста:

      for ((x=1; x<256; x++)); do
  hex="$(printf "%02x" $x)"
  name="$(printf \\x${hex})"
  if [ "${x}" = "10" ]; then
    name=$'\n'
  fi
  if [ "$(echo -n "${name}" | xxd | awk '{print $2}')" != "${hex}" ]; then
    echo "$x failed first sanity check"
  fi
  (
    eval "function ${name}(){ echo ${x};}" &>/dev/null
    if test "$("${name}" 2>/dev/null)" != "${x}"; then
      eval "function ok${name}doe(){ echo ${x};}" &>/dev/null
      if test "$(type -t okdoe 2>/dev/null)" = "function"; then
        echo "${x} failed second sanity test"
      fi
      if test "$("ok${name}doe" 2>/dev/null)" != "${x}"; then
        echo "${x}(${name}) never works"
      else
        echo "${x}(${name}) cannot be first"
      fi
    else
      # Just assume everything over 128 is hard, unless this says otherwise
      if test "${x}" -gt 127; then
        if declare -pF | grep -q "declare -f \x${hex}"; then
          echo "${x} works, but is actually not difficult"
          declare -pF | grep "declare -f \x${hex}" | xxd
        fi
      elif ! declare -pF | grep -q "declare -f \x${hex}"; then
        echo "${x} works, but is difficult in bash"
      fi
    fi
  )
done

Некоторые дополнительные примечания:

  • Символы 1-31 не идеальны, так как их труднее набирать.
  • Символы 128-255 даже не идеальны в bash (кроме bash 3.2 в macOS. Может быть, он скомпилирован по-другому?), Потому что такие команды, как declare -pFне отображать специальные символы, даже если они есть в памяти. Это означает, что любой код интроспекции неправильно предполагает, что этих функций нет. Однако такие функции, как compgen по-прежнему правильно отображать символы.
  • Вне области моего тестирования, но некоторые юникоды тоже работают, хотя в macOS очень сложно вставлять / печатать по ssh.

Из 3.3 Функции оболочки:

Функции оболочки - это способ группировать команды для последующего выполнения, используя одно имя для группы. Они выполняются как обычная команда. Когда имя функции оболочки используется в качестве простого имени команды, выполняется список команд, связанных с этим именем функции. Функции оболочки выполняются в текущем контексте оболочки; не создан новый процесс для их интерпретации.

Функции объявлены с использованием этого синтаксиса:

name () compound-command [ redirections ]

или же

function name [()] compound-command [ redirections ]

и из 2 определений:

название

Слово, состоящее только из букв, цифр и символов подчеркивания и начинающееся с буквы или символа подчеркивания. Имена используются как имена переменных и функций оболочки. Также называется идентификатором.

Этот скрипт проверяет все допустимые символы для имен функций с 1 символом.


Он выводит 53 действительных символа (a-zA-Z и подчеркивание), используя
оболочка POSIX и 220 действительных символов с BASH v4.4.12.

Ответ от Рона Бурка действителен, но в нем отсутствуют цифры.

#!/bin/sh

FILE='/tmp/FOO'
I=0
VALID=0

while [ $I -lt 256 ]; do {
        NAME="$( printf \\$( printf '%03o' $I ))"
        I=$(( I + 1 ))

        >"$FILE"
        ( eval "$NAME(){ rm $FILE;}; $NAME" 2>/dev/null )

        if [ -f "$FILE" ]; then
                rm "$FILE"
        else
                VALID=$(( VALID + 1 ))
                echo "$VALID/256 - OK: $NAME"   
        fi
} done
Другие вопросы по тегам