我如何调试 Bash 一个值 returns 的函数,以及如何向变量添加换行符?

How can I debug a Bash function that returns a value, and how can I add newlines to a variable?

这是一个 Bash 函数 returns a value,通过 echo:

#!/bin/bash
get_hello_name() {
  echo 'Hello !'
}
msg=$(get_hello_name "x")
echo $msg

输出:

$ bash ./initial_script5.sh
Hello !

然后我错误地认为返回了 last 回声(我来自 Java and Python),并试图使用回声来调试函数的其余部分。

然后我想知道为什么我无法在回显语句中打印出换行符,尽管尝试了 this question 中的每一个建议

这个脚本演示了问题:

#!/bin/bash
a_function() {
  echo "---In function"

  printf %"s\n" hello world
  printf "Hello\nworld"
  echo $'hello\nworld'
  echo -e 'hello\nworld'
}

echo "---Pre function"
printf %"s\n" hello world
printf "Hello\nworld"
echo $'hello\nworld'
echo -e 'hello\nworld'

x=$(a_function "x")
echo $x

echo "---Post function"
printf %"s\n" hello world
printf "Hello\nworld"
echo $'hello\nworld'
echo -e 'hello\nworld'

$ bash ./initial_script5.sh
---Pre function
hello
world
Hello
worldhello
world
hello
world
---In function hello world Hello worldhello world hello world
---Post function
hello
world
Hello
worldhello
world
hello
world

问题是函数中所有的echo被拼接在一起,经过单独裁剪,然后作为a返回整个。

所以这让我想到了两个问题:如何调试一个 returns 值的函数,以及如何将换行符附加到变量上(并不是说我什至想做后者,必然,但我想了解一下)?

为什么换行符似乎从变量中消失了

换行符实际上保留在变量中。它们不显示,因为 echo 语句中的变量没有用双引号引起来。来自代码:

echo $x

当使用不带双引号的变量时,执行分词。在默认 $IFS (wiki entry on IFS) 下,这意味着所有白色 space 集合,包括换行符和制表符,都将替换为单个 space.

为避免这种情况,只需使用双引号,如:

echo "$x"

通过这个单一的改变,你的脚本的输出变成:

$ bash a,sh 
---Pre function
hello
world
Hello
worldhello
world
hello
world
---In function
hello
world
Hello
worldhello
world
hello
world
---Post function
hello
world
Hello
worldhello
world
hello
world

现在显示变量 x 中的换行符。

旁白:剩下st运行g的两个词在一起

请注意组合 worldhello 出现在一行中,因为这是代码要求的:

printf "Hello\nworld"
echo $'hello\nworld'

printf 不会在 world 之后打印换行符。因此,world 与后面的 hello 出现在同一行。

详细记录

man bash 解释双引号禁止分词:

If the substitution appears within double quotes, word splitting and pathname expansion are not performed on the results.

分词发生变量扩展后,命令 替换和算术展开:

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

另一个微妙之处是,只有在发生某些替换时才会执行分词:

Note that if no expansion occurs, no splitting is performed.

通常情况下,当进行分词时,space、制表符和换行符的所有字符串都被替换为单个space。可以通过更改 IFS 变量的值来更改此默认行为:

The shell treats each character of IFS as a delimiter, and splits the results of the other expansions into words on these characters. If IFS is unset, or its value is exactly , the default, then sequences of space, tab, and newline at the beginning and end of the results of the previous expansions are ignored, and any sequence of IFS characters not at the beginning or end serves to delimit words. If IFS has a value other than the default, then sequences of the whitespace characters space and tab are ignored at the beginning and end of the word, as long as the whitespace character is in the value of IFS (an IFS whitespace character). Any character in IFS that is not IFS whitespace, along with any adjacent IFS whitespace characters, delimits a field. A sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.

如何调试

  1. 使用set -x

    将行 set -x 放在您希望 运行 的代码的开头。每行的评估结果将显示为函数 运行,每个前面有 PS4(默认为 +,space)以区别于正常输出。

    可以通过包含行 set +x.

    来关闭调试输出

    set -xset +x 都可以在命令行上工作。

  2. 使用stderr

    将调试输出发送到 stderr(文件描述符 2),如下所示:

    echo "My Debug Info" >&2
    

    默认情况下,管道和命令替换仅在 stderr 上运行。因此,默认情况下,发送到 stderr 的信息将出现在终端上。

更多关于echo

默认情况下,echo 忽略转义字符,序列 \n 仅表示 \ 后跟 n:

$ echo "Hello\nworld 4"
Hello\nworld 4

要将 \n 解释为换行符,请使用 -e:

$ echo -e "Hello\nworld 4"
Hello
world 4

跟随之后的工作代码(注意换行符打印在“1”行,而不是“4”,因为e选项):

#!/bin/bash
a_function() {
    echo -e "Hello\nworld\n 1"
    echo "Hello"
    echo "world"
    echo "Hello\nworld 4"
}

echo -e "Hello\nworld\n 1"
echo "Hello"
echo "world"
echo "Hello\nworld 4"

x=$(a_function "x")
echo "x-no-quotes>"
echo $x                       # No new lines!
echo "<x-no-quotes"

echo "x-in-double-quotes>"
echo "$x"                     # Yes new lines!
echo "<x-in-double-quotes"

echo -e "Hello\nworld\n 1"
echo "Hello"
echo "world"
echo "Hello\nworld 4"

输出:

Hello
world
 1
Hello
world
Hello\nworld 4
x-no-quotes>
Hello world 1 Hello world Hello\nworld 4
<x-no-quotes
x-in-double-quotes>
Hello
world
 1
Hello
world
Hello\nworld 4
<x-in-double-quotes
Hello
world
 1
Hello
world
Hello\nworld 4

您可能想用双引号来调用它:

echo "$x"

但是,如果您想明确显示您在其中键入的内容(也称为文字表达式),请使用单引号:

echo '$x'