可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
A contrived example... given
FOO="/foo/bar/baz"
this works (in bash)
BAR=$(basename $FOO) # result is BAR="baz" BAZ=${BAR:0:1} # result is BAZ="b"
this doesn't
BAZ=${$(basename $FOO):0:1} # result is bad substitution
My question is which rule causes this [subshell substitution] to evaluate incorrectly? And what is the correct way, if any, to do this in 1 hop?
回答1:
First off, note that when you say this:
BAR=$(basename $FOO) # result is BAR="baz" BAZ=${BAR:0:1} # result is BAZ="b"
the first bit in the construct for BAZ
is BAR
and not the value that you want to take the first character of. So even if bash allowed variable names to contain arbitrary characters your result in the second expression wouldn't be what you want.
However, as to the rule that's preventing this, allow me to quote from the bash man page:
Then a bit later:
And later when it defines the syntax you're asking about:
${parameter:offset:length} Substring Expansion. Expands to up to length characters of parameter starting at the character specified by offset.
So the rules as articulated in the manpage say that the ${foo:x:y}
construct must have a parameter as the first part, and that a parameter can only be a name, a number, or one of the few special parameter characters. $(basename $FOO)
is not one of the allowed possibilities for a parameter.
As for a way to do this in one assignment, use a pipe to other commands as mentioned in other responses.
回答2:
Modified forms of parameter substitution such as ${parameter#word}
can only modify a parameter, not an arbitrary word.
In this case, you might pipe the output of basename
to a dd command, like
BAR=$(basename -- "$FOO" | dd bs=1 count=1 2>/dev/null)
(If you want a higher count, increase count
and not bs
, otherwise you may get fewer bytes than requested.)
In the general case, there is no way to do things like this in one assignment.
回答3:
It fails because ${BAR:0:1}
is a variable expansion. Bash expects to see a variable name after ${
, not a value.
I'm not aware of a way to do it in a single expression.
回答4:
As others have said, the first parameter of ${} needs to be a variable name. But you can use another subshell to approximate what you're trying to do.
Instead of:
BAZ=${$(basename $FOO):0:1} # result is bad substitution
Use:
BAZ=$(_TMP=$(basename $FOO);${_TMP:0:1}) # this works
回答5:
${string:0:1},string must be a variable name
for example:
FOO="/foo/bar/baz"
baz="foo"
BAZ=eval echo '${'"$(basename $FOO)"':0:1}'
echo $BAZ
the result is 'f'
回答6:
A contrived solution for your contrived example:
BAZ=$(expr $(basename $FOO) : '\(.\)')
as in
$ FOO=/abc/def/ghi/jkl $ BAZ=$(expr $(basename $FOO) : '\(.\)') $ echo $BAZ j