Как отладить функцию bash, которая возвращает значение, и как добавить символы новой строки в переменную?

Сегодня я нахожусь в крушении bash.

Это функция bash, которая возвращает значение через echo:

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

Вывод:

$ bash ./initial_script5.sh
Hello $1!

Затем я неправильно подумал, что последнее эхо было возвращено (я пришел из Java и Python) и пытался использовать эхо для отладки остальной функции.

И тогда мне стало интересно, почему черт не могу напечатать новые строки в моих эхо-заявлениях, несмотря на попытку каждого предложения в этом вопросе.

Этот script демонстрирует проблему:

#!/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

Проблема состоит в том, что все эхо-функции в функции объединены вместе, после того, как они были индивидуально обрезаны, а затем возвращены как целое.

Итак, это приводит меня к двум вопросам: как вы отлаживаете функцию, возвращающую значение, и как вы добавляете новые строки в переменную (не то, что я даже хочу сделать последнее, обязательно, но я хотел бы понять она)?

Ответ 1

Почему символы новой строки, казалось, исчезли из переменной

Символы новой строки фактически сохраняются в переменной. Они не отображаются, потому что переменная в выражении echo не заключена в двойные кавычки. Из кода:

echo $x

При использовании переменной без двойных кавычек выполняется разбиение слов. По умолчанию $IFS (вики-запись в IFS), это означает, что все коллекции пробелов, включая новые строки и вкладки, заменяются на одно пространство.

Чтобы этого избежать, просто используйте двойные кавычки, как в:

echo "$x"

С этим единственным изменением вывод вашего script становится:

$ 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.

Кроме того: два слова, которые остаются натянутыми вместе

Обратите внимание, что комбинация worldhello отображается в одной строке, потому что это то, о чем просил код:

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

printf не печатает новую строку после world. Следовательно, world появляется в той же строке, что и следующая hello.

Документация сведений

man bash объясняет, что двойные кавычки блокируют расщепление слов:

Если подстановка появляется в двойных кавычках, разбиение слов и расширение пути не выполняются по результатам.

Разделение слов происходит после расширения переменной, команды подстановка и арифметическое расширение:

Оболочка сканирует результаты разложения параметров, команду замещения и арифметического расширения, которые не возникали внутри двойные кавычки для разбиения слов.

Еще одна тонкость заключается в том, что разбиение слова выполняется только в том случае, если имела место замещение:

Обратите внимание, что если разложение не происходит, расщепление не выполняется.

Обычно, когда выполняется разбиение слов, все строки пробелов, вкладок и строк новой строки заменяются одним пробелом. Это поведение по умолчанию можно изменить, изменив значение переменной IFS:

Оболочка рассматривает каждый символ IFS как разделитель и разбивает результаты других расширений на слова по этим символам. Если IFS не установлен, или его значение в точности соответствует по умолчанию, затем последовательности пространства, табуляции и новой строки на начало и конец результатов предыдущих расширений игнорируется, и любая последовательность символов IFS не в начале или конец служит для разграничения слов. Если IFS имеет значение, отличное от по умолчанию, затем пробелы пробелов и вкладки игнорируются в начале и в конце слова, пока символ пробела находится в значении IFS (пробел IFS персонаж). Любой символ в IFS, который не является пробелом IFS, вдоль с любыми смежными символами пробела IFS, ограничивает поле. последовательность символов пробелов IFS также рассматривается как разделитель. Если значение IFS равно нулю, словосочетание не происходит.

Отладка

  • Используйте set -x

    Поместите строку set -x в начало кода, который вы хотите запустить. Результаты оценки каждой строки будут отображаться по мере запуска функции, каждая из которых предшествует PS4 (по умолчанию это +, пробел), чтобы отличить ее от обычного вывода.

    Отладочный вывод можно отключить, включив в него строку set +x.

    set -x и set +x оба работают также в командной строке.

  • Используйте stderr

    Отправлять вывод отладки в stderr (дескриптор файла 2) следующим образом:

    echo "My Debug Info" >&2
    

    По умолчанию конвейеры и подстановки команд работают только на stderr. Следовательно, информация, отправленная на stderr, по умолчанию будет отображаться на терминале.

Подробнее о echo

По умолчанию echo игнорирует escape-символы, а последовательность \n просто означает a \, за которой следует n:

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

Чтобы \n интерпретировался как новая строка, используйте -e:

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

Ответ 2

Рабочий код после следующих советов John1024 (обратите внимание, что новые строки печатаются в строках "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

Ответ 3

Возможно, вы захотите назвать его двойным кавычкой echo "$x" Однако, если вы хотите явно показать, что вы набрали внутри, также известный как буквальное выражение, используйте одиночную кавычку echo '$x'