Bash 插入“;本地”;在shell脚本中的变量名导致错误之前

Bash 插入“;本地”;在shell脚本中的变量名导致错误之前,bash,sh,Bash,Sh,这段代码(它是shell函数的一部分)可以完美地工作: output=$(\ cat "${vim_file}" | \ sed -rne "${EXTRACT_ENTITIES}" | \ sed -re "${CLEAR_LEADING_QUOTES}" | \ sed -re "${NORMALIZE_NAMES}&q

这段代码(它是shell函数的一部分)可以完美地工作:

    output=$(\
            cat "${vim_file}" | \
            sed -rne "${EXTRACT_ENTITIES}" | \
            sed -re "${CLEAR_LEADING_QUOTES}" | \
            sed -re "${NORMALIZE_NAMES}" \
    )
但是当我试图在作业前插入“本地”一词时

    local output=$(\
            cat "${vim_file}" | \
            sed -rne "${EXTRACT_ENTITIES}" | \
            sed -re "${CLEAR_LEADING_QUOTES}" | \
            sed -re "${NORMALIZE_NAMES}" \
    )
…我得到一个奇怪的错误:

local: commands.: bad variable name

代码中没有错误的不可见字符:只有制表符在其他位置进行缩进和空格。脚本以“#!/bin/sh”开头。在函数中的其他变量之前插入“local”不会导致任何问题。用另一个任意字符串替换“output”(变量的名称)不会改变任何内容。操作系统是Linux。

非常简短的回答:使用更多的引号

local output="$(\
        cat "${vim_file}" | \
        sed -rne "${EXTRACT_ENTITIES}" | \
        sed -re "${CLEAR_LEADING_QUOTES}" | \
        sed -re "${NORMALIZE_NAMES}" \
)"
export var="$othervar"         # Safe in all shells
local var2="$(somecommand)"    # also safe
更详细的回答:双引号引用变量引用和命令替换几乎总是一个好主意。双引号可以防止它们受到分词和文件名通配符扩展的影响,这很少是您想要的,并且可能会导致混淆问题

在某些情况下,不使用双引号是安全的,但是规则很混乱,很难记住,而且很容易出错。这是一个令人困惑的案例。不发生分词和通配符扩展的情况之一(因此可以安全地取消双引号)位于赋值的右侧:

var=$othervar           # safe to omit double-quotes
var2=$(somecommand)     # also safe
var="$othervar"          # this also works fine
var2="$(somecommand)"    # so does this
有些shell将此扩展到作为命令一部分的分配,如
local
export

export var=$othervar         # *Maybe* ok, depending on the shell
local var2=$(somecommand)    # also *maybe* ok
bash将它们视为一种赋值类型,因此它不会对这些值执行拆分展开操作。但是dash更像是一个常规命令(参数会被拆分展开),所以如果脚本在dash下运行,它可能会出现类似这样的问题

例如,假设
somecommand
打印“export和local是shell命令”。然后在dash中,
local var2=$(somecommand)
将扩展为:

local var2=export and local are shell commands.
…它将声明局部变量
var2
(设置为“导出”)、
local
shell
。它还将尝试将
命令声明为局部变量,但失败,因为它不是合法的变量名

因此,使用更多的引号

local output="$(\
        cat "${vim_file}" | \
        sed -rne "${EXTRACT_ENTITIES}" | \
        sed -re "${CLEAR_LEADING_QUOTES}" | \
        sed -re "${NORMALIZE_NAMES}" \
)"
export var="$othervar"         # Safe in all shells
local var2="$(somecommand)"    # also safe
或者将声明分开(或者两者都分开!):


查看错误消息:您提供的变量名无效:

$sh
$foo(){local commands.;commands=5;echo“${commands}”;}
$foo
sh:local:`commands.':不是有效的标识符
5.

答案在这里:

这是这里的引文:

正如Evgeniy Ivanov指出的,当在单个命令中声明和设置局部变量时,显然操作顺序是首先设置变量,然后才将其限制在局部范围内

这意味着,如果局部变量包含空格,则尝试执行
local
命令时,shell将只接受赋值的第一个字。字符串的其余部分将根据内容进行解释

shell解释其余内容的方式对我来说仍然是个谜。在我的例子中,它试图使用正在读取的文件的任意部分来执行赋值。例如,错误消息中的“commands.”字符串是
cat
命令所操作的一个文件中的一个句子的结尾

因此,有两种方法可以解决这个问题

第一个是分配任务。也就是说,不是

local output=$(cat ...
……它必须是:

local output
output=$(cat ...
第二种方法是从问题下的评论中得出的——对整个表达式使用了周围的引号:

local output="$(cat...)"
总结:使用shell时,我们都必须始终记住在空间中隐藏的拆分


请阅读Gordon Davisson的文章。

我发现,当声明分为两行时,它可以正常工作:本地输出↲ 输出=$(cat…作为旁白,您的
sed
脚本链可能应该组合在一起。在某些情况下,这是不正确的,但通常情况下,任何看起来像
sed a | sed b | sed c
的东西都可以组合到
sed'a;b;c'
。您也会希望丢失。为什么所有的反斜杠?您不需要明确地添加它们在sane shells中(seashells在这方面并不健全)。更重要的是,@oneastok,当shell在一行末尾看到一个(未开槽,无引号)
时,它知道必须有另一个命令跟随它,所以它会继续寻找一个命令。您在
$(…)中使用了反斜杠
命令替换,这些是(圆)括号或圆括号
()
,而不是花括号或大括号
{}
(它们都不同于(方)括号
[]
,以及“尖括号”
)@oneastok不是大括号使反斜杠在行尾变得不必要,而是管道(请参阅)。此外,关于
sed
,您也可以使用
sed-re“$command1”-e“$command2”-e“$command3”…
(尽管您需要继续反斜杠来打破此状态)(它可能不适用于第一个
sed
命令,您可以在其中过滤行)。这是一个非常出色的答案。感谢您抽出时间提醒我使用引号。(我在发布自己的答案后刚刚阅读了您的答案。)Thaks感谢您提供答案。然而,我们在推荐ABS时有点谨慎;也许有人可以提供一个更规范、更可靠的参考。@tripleee,我使用了第一个解释原因的链接。该链接有什么问题?是网站不可信还是孟德尔·库珀的书?(我过去常通过那本书学习Bash)我还没有详细看过这个链接。ABS很受欢迎,但有点可疑,有点像Bash的W3schools。