使用子shell和子字符串来解决Bash中的坏替换问题

24

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

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次操作中完成?

6个回答

15

首先要注意,当你说这句话时:

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)不是允许作为参数的选项之一。

至于如何在一次赋值中完成此操作,请使用其他响应中提到的管道到其他命令的方式。


7

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

在这种情况下,您可以将basename的输出导入到dd命令中,例如:

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

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

在一般情况下,没有办法在一个赋值语句中完成这样的操作。


6
它失败是因为${BAR:0:1}是一个变量扩展。Bash期望在${后看到一个变量名,而不是一个值。
我不知道有一种方法可以用单个表达式实现。

4

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

不要这样做:

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

使用:

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

1
一种人为的解决方案适用于你所构造的例子:

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

如同

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

0

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

例如:

FOO="/foo/bar/baz"

baz="foo"

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

echo $BAZ

结果为'f'


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接