Подтвердить что ты не робот

Bash плохая подстановка с подоболочкой и подстрокой

Надуманный пример... учитывая

FOO="/foo/bar/baz"

это работает (в bash)

BAR=$(basename $FOO) # result is BAR="baz"
BAZ=${BAR:0:1}       # result is BAZ="b"

это не

BAZ=${$(basename $FOO):0:1} # result is bad substitution

Мой вопрос: какое правило приводит к неправильной оценке этой [подзаголовки]? И каков правильный способ, если таковой имеется, сделать это в 1 прыжке?

4b9b3361

Ответ 1

Прежде всего, обратите внимание, что, когда вы это говорите:

BAR=$(basename $FOO) # result is BAR="baz"
BAZ=${BAR:0:1}       # result is BAZ="b"

первый бит в конструкции для BAZ - BAR, а не значение, которое вы хотите перенести первым символом. Поэтому даже если bash допустимые имена переменных содержат произвольные символы, ваш результат во втором выражении не будет тем, что вы хотите.

Однако, что касается правила, предотвращающего это, позвольте мне привести цитату из справочной страницы bash:

DEFINITIONS
       The following definitions are used throughout the rest  of  this  docu‐
       ment.
       blank  A space or tab.
       word   A  sequence  of  characters  considered  as a single unit by the
              shell.  Also known as a token.
       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.

Затем немного позже:

PARAMETERS
       A parameter is an entity that stores values.  It can be a name, a  num‐
       ber, or one of the special characters listed below under Special Param‐
       eters.  A variable is a parameter denoted by a name.  A variable has  a
       value  and  zero or more attributes.  Attributes are assigned using the
       declare builtin command (see declare below in SHELL BUILTIN COMMANDS).

И позже, когда он определяет синтаксис, о котором вы спрашиваете:

   ${parameter:offset:length}
          Substring Expansion.  Expands to  up  to  length  characters  of
          parameter  starting  at  the  character specified by offset.

Таким образом, правила, сформулированные в manpage, говорят о том, что конструктор ${foo:x:y} должен иметь параметр в качестве первой части и что параметр может быть только именем, числом или одним из нескольких символов специальных параметров. $(basename $FOO) не является одной из разрешенных возможностей для параметра.

Что касается способа сделать это в одном задании, используйте канал для других команд, как указано в других ответах.

Ответ 2

Модифицированные формы подстановки параметров, такие как ${parameter#word}, могут изменять только параметр, а не произвольное слово.

В этом случае вы можете передать вывод basename в команду dd, например

BAR=$(basename -- "$FOO" | dd bs=1 count=1 2>/dev/null)

(Если вы хотите увеличить счетчик, увеличьте count, а не bs, в противном случае вы можете получить меньше байтов, чем запрошено.)

В общем случае нет способа сделать что-то подобное в одном задании.

Ответ 3

Он терпит неудачу, потому что ${BAR:0:1} - это переменное расширение. Bash ожидает увидеть имя переменной после ${, а не значение.

Я не знаю, как это сделать в одном выражении.

Ответ 4

Как уже говорили другие, первый параметр $ {} должен быть именем переменной. Но вы можете использовать другой подоболочек, чтобы приблизиться к тому, что вы пытаетесь сделать.

Вместо:

BAZ=${$(basename $FOO):0:1} # result is bad substitution

Использование:

BAZ=$(_TMP=$(basename $FOO); echo ${_TMP:0:1}) # this works

Ответ 5

Надуманное решение для вашего надуманного примера:

BAZ=$(expr $(basename $FOO) : '\(.\)')

как в

$ FOO=/abc/def/ghi/jkl
$ BAZ=$(expr $(basename $FOO) : '\(.\)')
$ echo $BAZ
j

Ответ 6

${строка: 0: 1}, строка должна быть именем переменной

например:

FOO = "/Foo/бар/Baz"

Баз = "Foo"

БАЗ = eval echo '${'"$(basename $FOO)"':0:1}'

echo $BAZ

результат равен 'f'