为什么将带引号的字符串传递给 Bash 函数并用作命令参数不能按预期工作?

Why does passing a quoted string to Bash function and using as command parameter doesn't work as expected?

我有这段代码调用 dialog 参数,应该创建一个只有一个条目的菜单:

!/bin/bash
   
menu() {
        echo dialog --clear --stdout --title "Menu" --menu "menu" 0 0 0   # dialog --clear --stdout --title Menu --menu menu 0 0 0 1 "my menu item" 1
        dialog --clear --stdout --title "Menu" --menu "menu" 0 0 0  
}

echo $(menu "1 \"my menu item\"")

它确实创建了菜单,但它创建了 2 个菜单项而不是一个,并为菜单项添加了双引号。


如果我使用此代码,它会起作用:

!/bin/bash
   
echo dialog --clear --stdout --title "Menu" --menu "menu" 0 0 0 1 "my menu item"  # dialog --clear --stdout --title Menu --menu menu 0 0 0 1 my menu item
dialog --clear --stdout --title "Menu" --menu "menu" 0 0 0 1 "my menu item"

可以看出,即使在第一种情况下菜单项参数用双引号括起来(见 echo),它也被解释为 3 个参数。

如何使第一个示例按预期工作?

试试这样的东西:

#!/usr/bin/env bash

menu() {
  dialog \
    --clear \
    --stdout \
    --title "Menu" \
    --menu "menu" 0 0 0 \
    "$@" 
}

choice=$(
  menu \
    1 'my menu item' \
    2 'my other menu item'
)

if [ -n "$choice" ]; then
  printf 'Choice is %s\n.' "$choice"
else
  echo 'Cancelled!'
fi

Why does passing a quoted string to Bash function and using as command parameter doesn't work as expected?

我不认为参数传递的工作方式与您预期的不同,而是 command-line 扩展,尤其是参数扩展。让我们举一个更说明性的例子:

analyze2() {
  echo -n "  $# words: "
  for word; do
    echo -n " $word"
  done
  echo
}

analyze() {
  echo arg count: $#
  for arg; do
    echo $arg
    analyze2 $arg
    analyze2 "$arg"
  done
}

现在称呼它:

$ analyze foo "bar baz" "\"African or European swallow?\""
arg count: 3
foo
  1 words:  foo
  1 words:  foo
bar baz
  2 words:  bar baz
  1 words:  bar baz
"African or European swallow?"
  4 words:  "African or European swallow?"
  1 words:  "African or European swallow?"

要记住的要点之一是,在命令中字面上出现的引号与参数扩展产生的引号之间存在差异。只有前者具有任何句法相关性,防止单词拆分和引号删除。参数扩展产生的报价只是数据。

How can I make the first example work as expected?

如果您希望 menu 函数将两个参数传递给 dialog,则将两个参数传递给 it。在函数内部,您可以使用特殊参数 $*$@ 之一传递完整的参数列表。它们的不同之处在于在双引号内扩展它们的效果:"$*" 将所有参数扩展为一个 shell 单词,而 "$@" 将每个参数扩展为单独的 shell 单词.

!/bin/bash
   
menu() {
        echo dialog --clear --stdout --title "Menu" --menu "menu" 0 0 0 "$@"  # dialog --clear --stdout --title Menu --menu menu 0 0 0 1 "my menu item" 1
        dialog --clear --stdout --title "Menu" --menu "menu" 0 0 0 "$@" 
}

echo $(menu 1 "my menu item")