在函数内部定义变量

在函数内部定义变量

在阅读了该互联网上的某处之后,我发现将命令的输出用作变量时最好执行以下操作:

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/fileor /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

相关内容