Плохая замена Bash на подоболочку и подстроку - PullRequest
19 голосов
/ 07 мая 2011

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

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 прыжке?

Ответы [ 6 ]

11 голосов
/ 07 мая 2011

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

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.

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

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

6 голосов
/ 07 мая 2011

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

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

6 голосов
/ 07 мая 2011

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

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

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

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

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

2 голосов
/ 06 марта 2016

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

Вместо:

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

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

BAZ=$(_TMP=$(basename $FOO); echo ${_TMP:0:1}) # this works
0 голосов
/ 16 июня 2017

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

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

как в

$ FOO=/abc/def/ghi/jkl
$ BAZ=$(expr $(basename $FOO) : '\(.\)')
$ echo $BAZ
j
0 голосов
/ 13 февраля 2014

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

, например:

FOO = "/ foo / bar / baz"

baz= "foo"

BAZ = eval echo '${'"$(basename $FOO)"':0:1}'

echo $ BAZ

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

...