在阅读了该互联网上的某处之后,我发现将命令的输出用作变量时最好执行以下操作:
FILE_CORE_NAME="$(/usr/bin/env basename $(/usr/bin/env awk -F "." '{print $1}' <<< "${FILE_TO_PROCESS}"))"; local FILE_CORE_NAME;
但是,当我使用此配置时,FILE_CORE_NAME 派生的值为空。如果我local FILE_CORE_NAME
从行中删除尾随,一切都会按预期进行。
是我读错了还是我做错了?
答案1
FILE_CORE_NAME="$(/usr/bin/env basename $(/usr/bin/env awk -F "." '{print $1}' <<< "${FILE_TO_PROCESS}"))"; local FILE_CORE_NAME;
即使为了易读而将其重新格式化为几行
FILE_CORE_NAME="$(
/usr/bin/env basename $(
/usr/bin/env awk -F "." '{print $1}' <<< "${FILE_TO_PROCESS}"
)
)"
local FILE_CORE_NAME
许多帐户都是错误的:
awk
{print $1}
对其输入的每一行运行该命令,因此如果您有一个$'/a.b/c\nd.e\nf/g.h.i'
文件,它将输出/a<newline>d<newline>f/g'
.换行符与任何字符一样有效,并且文件名不保证是文本,因此您不应使用基于行的文本实用程序来处理它们。- 如上所述,即使对于单行文件路径,如果您有 a
/foo.d/file.txt
或/foo.bar.2020.jpg
文件,您将得到/foo
而不是/foo.d/file
or/foo.bar.2020
。应该首先完成基本名称,它是.
您要检索的直到最后一个的部分。 - 第二个周围省略了引号
$(...)
,因此它受到 split+glob 的影响可能造成的灾难性后果。 --
之后缺少选项分隔符,basename
因此如果文件路径以 a 开头,-
则会失败。local
就是声明一个函数的局部变量。然而在这里,你定义它local
后修改了它,所以你修改了函数调用者的变量。然后在 bash 中(与内置函数的 ash 相反local
),除非locvar_inherit
已启用该选项,否则local var
声明变量为 local 但不设置任何值。- 所有大写变量名称都应保留为环境变量名称,或者至少保留在整个脚本和依赖项中具有全局范围的变量名称。
- 我看不出在这里使用的意义
/usr/bin/env
。
在这里,显而易见的事情是切换到 zsh 并执行以下操作:
local core_name=$FILE_TO_PROCESS:t:r
其中,就像 70 年代的 csh 或 vim(甚至 bash 历史替换,但不是其参数扩展)一样,:t
为您提供尾部名称和:r
根名称(删除了一个扩展名)。
如果你必须使用bash
,你可以这样做:
local core_name
core_name=$(basename -- "$FILE_TO_PROCESS") || return
core_name=${core_name%.*}
(这也适用于所有具有local
内置功能的 Ash 类 shell)。
请记住,$(...)
删除尾随换行符。
请注意,例如 if $FILE_TO_PROCESS
,/home/me/.zshrc
将$core_name
变成空字符串。您可能想要防范该值以及其他一些特殊值,例如-
,.
或..
取决于您想$core_name
在函数的其余部分中使用它做什么。
答案2
local name=value
创建一个局部变量并将其设置为值。在您的情况下,没有值,因此您创建一个空的局部变量,并且该局部变量隐藏与存储命令输出同名的非局部变量。
所以需要先将变量声明为local,然后再给它赋值。
以下片段:
func() {
x=4
local x
echo "func: $x"
}
x=1
func
echo "outside: $x"
它打印
func:
outside: 4