string 使用子外壳和子字符串进行 Bash 坏替换

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5917439/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 01:05:38  来源:igfitidea点击:

Bash bad substitution with subshell and substring

bashstringsubstitutionsubshell

提问by Matt

A contrived example... given

一个人为的例子......给出

FOO="/foo/bar/baz"

this works (in bash)

这有效(在 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?

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

采纳答案by Daniel Martin

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 BAZis BARand not the valuethat 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.

在构建第一位BAZIS BAR,而不是价值要采取的第一个字符。因此,即使 bash 允许变量名称包含任意字符,您在第二个表达式中的结果也不会是您想要的。

However, as to the rule that's preventing this, allow me to quote from the bash man page:

但是,对于阻止这种情况的规则,请允许我引用 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.

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.

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

As for a way to do this in one assignment, use a pipe to other commands as mentioned in other responses.

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

回答by jilles

Modified forms of parameter substitution such as ${parameter#word}can only modify a parameter, not an arbitrary word.

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

In this case, you might pipe the output of basenameto a dd command, like

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

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

(If you want a higher count, increase countand not bs, otherwise you may get fewer bytes than requested.)

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

In the general case, there is no way to do things like this in one assignment.

在一般情况下,没有办法在一项任务中做这样的事情。

回答by Alanyst

It fails because ${BAR:0:1}is a variable expansion. Bash expects to see a variable name after ${, not a value.

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

I'm not aware of a way to do it in a single expression.

我不知道有一种方法可以在单个表达式中做到这一点。

回答by bobpaul

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.

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

Instead of:

代替:

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

Use:

用:

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

回答by Larry

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

回答by user3304852

${string:0:1},string must be a variable name

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

for example:

例如:

FOO="/foo/bar/baz"

FOO="/foo/bar/baz"

baz="foo"

巴兹=“富”

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

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

echo $BAZ

回声 $BAZ

the result is 'f'

结果是'f'