Bash 使用 subshel​​l 和 substring 进行错误替换

发布于 11-05 17:50 字数 357 浏览 5 评论 0原文

一个人为的例子......考虑到

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

我的问题是哪个规则导致这个[子shell替换]评估不正确?在 1 跳中执行此操作的正确方法(如果有)是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

萌逼全场2024-11-12 17:50:39

首先,请注意,当您这样说时:

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.

因此,手册页中阐明的规则说 < code>${foo:x:y} 构造必须有一个参数作为第一部分,并且参数只能是名称、数字或少数特殊参数字符之一。 $(basename $FOO) 不是参数允许的可能性之一。

至于在一项作业中执行此操作的方法,请使用其他响应中提到的其他命令的管道。

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:

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.

Then a bit later:

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).

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.

紫竹語嫣☆2024-11-12 17:50:39

参数替换的修改形式,例如 ${parameter#word} 只能修改参数,不能修改任意单词。

在这种情况下,您可以将 basename 的输出通过管道传输到 dd 命令,例如

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

(如果您想要更高的计数,请增加 count 而不是 bs,否则您获得的字节数可能少于请求的字节数。)

在一般情况下,无法在一次作业中执行此类操作。

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.

小耗子2024-11-12 17:50:39

它失败是因为 ${BAR:0:1} 是变量扩展。 Bash 希望在 ${ 之后看到变量名,而不是值。

我不知道如何用单个表达式来做到这一点。

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.

死开点丶别碍眼2024-11-12 17:50:39

正如其他人所说, ${} 的第一个参数需要是变量名。但是您可以使用另一个子 shell 来近似您想要执行的操作。

代替:

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

使用:

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

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); echo ${_TMP:0:1}) # this works
乜一2024-11-12 17:50:39

针对您的人为示例的人为解决方案:

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

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

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
清泪尽2024-11-12 17:50:39

${string:0:1},string 必须是变量名

例如:

FOO="/foo/bar/baz"

baz="foo"

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

echo $BAZ

结果是 'f'

${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'

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文