Bash 插入“;本地”;在shell脚本中的变量名导致错误之前
这段代码(它是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
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。