有什么方法可以禁用 bash 中的分词吗?

有什么方法可以禁用 bash 中的分词吗?

我不是 bash 专家,但最近我一直在编写 bash 脚本,每次都引用我的参数真的很麻烦。有什么办法可以改变这种 shell 行为吗?我希望我的字符串即使不引用它们也不会被分割

PS - 将 IFS 设置为 null 不起作用,因为所有参数都会被破坏。我想要的是写作时:

func1() {
    echo $1 $2 $3
}

func2() {
    echo "number of arguments func2 got: $#"
} 

func2 $(func1 firstarg "second arg is a multi word string" "third arg is also a multi word string")

会打印:

number of arguments func2 got: 3

编辑,感谢评论指出:由于我的示例使用 echo,因此代码无论如何都会打印它得到 1 或 14 个参数,具体取决于您是否引用 func2 的参数。然而,我正在寻找一种从函数返回多个值的方法,而不会使它们被破坏,就像我提到的预期结果一样

谢谢!

答案1

要回答标题中的问题,禁用分词的方法是设置IFS为空字符串。

但是您在文本中描述的内容更困难,在通过命令替换传递多个多单词字符串后保持它们不同。确实没有简单的方法可以做到这一点。

这里的根本问题是命令替换的结果(〜在其中调用的函数的标准输出的输出)只是一个字符串(字节流),并且您试图在其中容纳多个字符串。在一般情况下,这些字符串中包含任意内容,这是一个相当基本的问题。

这也没有太大不同将命令存储在变量中,只是我们在这里进行了命令替换。


解决方法是保留一些字符用作分隔符,并将其设置IFS为该字符。使用默认值IFS,您将使用空格、制表符和换行符作为分隔符,这显然不适用于带有空格的字符串。如果IFS设置为空字符串,则不会有这样的分隔符,因此您总是只会得到一个字段。

但是您可以设置IFS为例如仅换行符(假设您的字符串不是多行):

#!/bin/bash
IFS=$'\n'
foo() {
    printf "%s\n" "$@"
}

nargs() {
    echo "number of arguments nargs got: $#"
} 

nargs $(foo firstarg "multi word string" "also a multi word string")

(但是,您不能在列表末尾使用空字符串,因为命令替换会删除所有尾随换行符,而不管IFS.)

另一种方法是不使用函数的标准输出,而是将变量的名称传递给函数,并让函数通过 nameref 写入命名数组:

#!/bin/bash
bar() {
    declare -n _out="$1"
    shift
    _out=("$@")
}

nargs() {
    echo "number of arguments nargs got: $#"
} 

bar tmparr firstarg "multi word string" "also a multi word string"
nargs "${tmparr[@]}"

(如果调用的变量_out也存在于函数外部,则变量作用域存在一些问题。)

还要注意,如果您有未加引号的扩展(变量或命令替换),它们的结果也将受到文件名通配符的影响,因此像这样的输出12 * 34将会变得严重混乱,除非您还使用 禁用通配符set -f

答案2

你原本想要的是参数扩展后禁用分词,所以不要写

mcd() {
  mkdir -p "$1"
  cd "$1"
}

你可以写

mcd() {
  mkdir -p $1
  cd $1
}

无论如何它都会起作用mcd "foo bar"

这是大多数用例的首选行为,这就是为什么zsh将其设为默认行为(您可以使用SH_SPLIT_WORD选项全局禁用它或通过编写单独禁用它${=1})。

我知道你要求bash解决方案,但是“使用zsh而不是bash“这是我许多问题的答案,以至于我真的有zsh一天切换到并摆脱了许多问题。因此,我很高兴将这一建议传递给其他人。恕我直言,坚持下去的理由很少bash

相关内容