Bash удалил переводы строк из переменной: что делает подстановка команды

Команда сформировала текст с двумя переводами строки в конце. После text=$(command) они исчезли, хотя кавычки стоят правильно. Для отчёта это может быть незаметно, но при проверке подписи или сравнении файлов меняются байты. Кавычки защищают значение от разбиения на слова; правила самой подстановки команды действуют раньше.

Bash убирает все завершающие переводы строк из результата $(...). Внутренние переводы сохраняются. Кроме того, переменная оболочки не подходит для произвольного двоичного содержимого с нулевыми байтами. Поэтому прежде чем копировать данные в переменную, решите, нужен ли вам текстовый параметр или точная последовательность байтов.

Сравним длину до и после

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

#!/usr/bin/env bash
folder=$(mktemp -d) || exit 1
file="$folder/message.txt"
printf 'alpha
beta

' > "$file"
text=$(cat -- "$file")
printf 'file bytes: '
wc -c < "$file"
printf 'variable bytes: '
printf '%s' "$text" | wc -c
printf 'stream bytes: '
cat -- "$file" | wc -c

Получатся длины 12, 10 и 12 байтов. Два завершающих LF потерялись только при помещении результата в переменную. Для показанного ASCII-текста число байтов совпадает с числом символов; в UTF-8 с кириллицей это уже разные величины.

Когда следующая команда умеет читать stdin, соедините её с производителем через pipe. Если нужны повторное чтение, контрольная сумма или передача файла внешней утилите, сохраните результат в файл и передайте путь. В обоих случаях не приходится восстанавливать потерянные окончания по догадке.

Почему printf лучше случайного echo

Восстановить текст через echo "$text" значит добавить один перевод строки. Исходный файл мог содержать два, один или ни одного, поэтому это не обратное преобразование. printf '%s' передаёт именно значение, но и он не вернёт байты, уже удалённые подстановкой.

Для одной логической строки удаление окончания обычно удобно: имя ветки, полученное от команды, становится параметром без LF. Проблема возникает, когда тем же приёмом переносят документ целиком. Назначение данных помогает выбрать представление ещё до появления ошибки.

Если переменная действительно нужна

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

Проверьте пустой текст, текст без последнего LF и текст с несколькими LF. Если ваша задача требует точности, сравнивайте файлы cmp или считайте контрольную сумму по потоку. Проверка на экране ненадёжна: терминал сам меняет положение курсора, а редактор может скрывать финальную пустую строку. Для генератора вложений или подписи полезнее измеримые байты, чем визуально одинаковая распечатка.

На курсе Bash Linux + Git вы последовательно разберёте переменные, потоки и перенаправления, затем соберёте собственные скрипты. Откройте программу, чтобы уверенно выбирать между аргументом, stdin и файлом.

Продолжить на практике