Почему командная подстановка оболочки поглощает завершающий символ новой строки?

25

Как в следующем примере, и как в моем недавнем вопросе в bash, куда ушел завершающий символ новой строки? Я хочу знать, "почему" это происходит

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

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

Peter.O
источник

Ответы:

22

Поскольку оболочка изначально не предназначалась для полноценного языка программирования.

Довольно сложно удалить трейлинг \nиз некоторых выводов команды. Однако для целей отображения почти все команды заканчивают вывод \n, поэтому… должен быть простой способ удалить его, если вы хотите использовать его в другой команде. Автоматическое удаление со $()строительством было выбрано решение.

Итак, возможно, вы примете этот вопрос в качестве ответа:

Можете ли вы найти простой способ удалить трейлинг, \nесли это не было сделано автоматически в следующей команде?

> echo The current date is "$(date)", have a good day!

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

Стефан Хименес
источник
1
Если то, что вы говорите, правда, то я был бы совершенно ошеломлен. Если есть shoptвозможность изменить описанное вами поведение по умолчанию, то я был бы рад снова ... Мне трудно думать, что Bash / Linux / Unix загрязнит такую ​​важную функцию, как "захват stdout" только потому, dateчто не имеет a с / без опции '\ n' ... Очевидным исправлением будет то, что bash (или другая оболочка) имеет shoptдля этого ... Знаете ли вы что-нибудь? ... а есть ли у вас ссылки, которые говорят о причинах и причинах этого вопроса? ... и почему нет dateопции \ n .. Должно быть!
Peter.O
2
Подстановки команд появились в первой версии оболочки Bourne в «7-м издании» Unix в 1979 году. Это была уже большая революция, и я думаю (я не родился), что никто не заботился о том, чтобы продолжать следовать за \ n или нет. Да, вы можете прочитать man-страницу менее чем за 10 минут, и, кажется, ничего не изменилось в отношении подстановки команд. За исключением предпочтительного $()альтернативного синтаксиса.
Стефан Гименес
Спасибо. Я подумал, что это может быть проблемой унаследованного, и это определенно зависит от того, что мне лучше начать более внимательно наблюдать за своими командными заменами. До сегодняшнего дня я должен был выжить в молитве ... Некоторые из этих "таинственных событий", с которыми я столкнулся, начинают обретать смысл :) ... Так что в обратном случае ваша позиция "даты", если Я хочу сохранить мой трейлинг \ n, я должен { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }
написать
PS Вот мой комментарий выше: Конечно, просто dateсработало бы, но я просто использовал dateв качестве примера любую команду ..
Peter.O
Ваш «вопрос» о дате был самым сильным побуждением к моему пониманию того, «почему» Command Substution делает то, что делает, так что это был «лучший» ответ на мой вопрос ... и мне, конечно же, нужны были и другие хорошие ответы. ..
Peter.O
19

Это часть стандарта :

Оболочка должна расширить подстановку команд, выполнив команду в среде подоболочек (см. Среда выполнения оболочки ) и заменив подстановку команд (текст команды плюс заключенные в нее «$ ()» или обратные кавычки) стандартным выводом команды, удалив последовательности из одного или нескольких <newline> в конце замены.

Конечно, стандарт, вероятно, написан таким образом, потому что вот как kshон это сделал или что-то в этом роде, но это стандарт и документированное поведение. Если вам это не нравится, используйте Perl или что-то еще, что позволит вам сохранить последние переводы строки.

Стивен Притчард
источник
Хорошее резюме .. Спасибо .. и ссылки, безусловно, помогли
Peter.O
4
Стандарт таков, потому что так делал это оболочка Bourne, как и все остальные.
Жиль "ТАК - перестань быть злым"
6

Ну, это имеет смысл для меня. Новая строка находится только на первом месте в обычном выводе команды, поэтому приглашение появляется после завершения команды в новой строке. В большинстве случаев новая строка не является частью исходного вывода, она помогает привести в порядок экран. Когда вы анализируете выходные данные команды, перевод строки в конце обычно вызывает проблемы. Почему wcкоманда выводит 2 строки текста? О, это не так, он выводит один, за которым следует новая строка. Когда вы анализируете, wcвы не хотите беспокоиться о том, что есть 2 строки вывода - на самом деле их нет, есть только одна.

EightBitTony
источник
@EightBitTony: Мой вопрос никоим образом не связан с отображением чего-либо в терминале. Это довольно просто. Если есть новая строка для отображения, то она будет отображать новую строку. Речь идет здесь о том, что \nв исходном stdoutпотоке удаляется подстановка команд. то есть. В моих исходных данных (очень важных данных) удаляются завершающие символы новой строки, даже когда данные заключены в "кавычки". Я достаточно хорошо понимаю, как и почему работает Word Splitting, но я совершенно не понимаю, почему подстановка команд удаляет только новые строки и только конечные .
Peter.O
1
Ничто из того, что ты сказал, не меняет моего взгляда. Чаще всего заключительный символ новой строки в любом выводе используется исключительно для целей отображения, а не как часть данных.
EightBitTony
Я согласен с вашим мнением, и все время имею в виду ... Да, новая строка в конце вывода предназначена для удобства ... но моя проблема не имеет к этому никакого отношения ... Я спрашиваю: почему подстановка команд принудительно удаляет ее ? ... Это две разные проблемы .. Может быть, мой вопрос должен быть: есть ли встроенный обходной путь? , потому что необходимость кодировать эту проблему, добавляя конечный фиктивный символ, отстой! ... Я сделаю это, если придется, но это первый раз, когда я зашел в тупик bash (и я нахожусь в состоянии шока :) .. Это так хорошо ...
Peter.O
Как уже упоминалось в другом ответе, это часть стандарта. Для меня, я думаю, это происходит именно так, потому что когда вы обрабатываете данные, вы хотите видеть только данные, а не удобный перевод строки. Это потому, что оболочка ожидает, что вы будете обрабатывать данные в конвейере, а символ новой строки не является данными. Больше, и мы должны принять это к обсуждению.
EightBitTony
Спасибо. У меня уже есть идея. Кажется, что случай подстановки команд просто склоняется к тому, чтобы сбрасывать завершающие символы новой строки, а не поддерживать их ... В этом шаге должна быть некоторая мудрость, которую я не чувствовал «пока, но я привык думать, что подавляющее популярность использования всех букв-регистров в именах файлов было немного странным / ненужным, но на днях я думал, что это на самом деле довольно удобная система… Я, наверное, см. (более подробно) рифму и причину поведения подстановки команд, когда-нибудь ...
Peter.O
1

Я столкнулся с той же проблемой, и нашел ниже пример. Кажется, цитирование помогло ситуации, по крайней мере, для меня:

http://tldp.org/LDP/abs/html/commandsub.html .

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh
christian_hercules
источник
2
Вот что здесь происходит. Допустим, у вас есть переменная $str, содержащая строку "a b c". Если вы пройдете $strк echoбез кавычек ( echo $str), echoбудет получать a, bи cкак три отдельные аргументы. Ваша оболочка не заботится о пробелах между аргументами. echoзатем напечатает эти аргументы с пробелами, разделяющими каждый, так что вы получите "a b c". Если вы передадите переменную в echoкавычки вокруг нее ( echo "$str"), оболочка увидит в ней один аргумент и echoполучит его как таковой, поэтому пробел не будет свернут. То же относится и к переводу строки.
1
Проблема, с которой сталкивается оригинальный постер, состоит в том, что последние строки (только самые последние) его команд исчезают. Если -nфлаг не пройден , echoк его выходу добавляется завершающий символ новой строки, поэтому оба эха в вашем примере содержат завершающие символы новой строки. Однако, если вы попробуете echo -n $dir_listingили echo -n "$dir_listing", вы увидите, что завершающий символ новой строки не выводится с кавычками или без них $dir_listing, что указывает на проблему с подстановкой команд.
2
tldp - сильно устаревшее место для изучения сценариев оболочки. Попробуйте Wooledge Bash Wiki. mywiki.wooledge.org/BashGuide
Wildcard
-1

почему echo "hello "(без кавычек) сожрать пространство? значение символов IFS рассматривается оболочкой как разделители, поэтому завершающие (которые ничего не разделяют) удаляются. Подстановка commend - это просто выполнение комментариев в под-оболочке, поэтому она следует той же логике.

человек, любящий учиться
источник
@Philomath: Оба x="hello  "и x="hello     "потеряют все свои завершающие пробелы, если они отображаются через echo $x... Однако потеряется только самая последняя новая строка Командного Подстановки.
Peter.O
странно, я не вижу никакой разницы в моем bash (проверка с помощью xxd)
Philomath
Я просто проверяю это ... Он удаляет все завершающие символы новой строки ... Я экспериментировал с IFS, когда у меня появилось такое впечатление, поэтому давайте отменим это :) ... но вопрос все еще остается ... Это фундаментальная часть разбиение слов для сжатия нескольких пробелов в один пробел и полного удаления начальных и конечных пробелов. Я могу это понять, но я, конечно, не понимаю, почему с помощью подстановки команд он удаляет только \ n, а не все пробелы (как с разделением слов), и почему только конечный конец? .. и прежде всего: "Подстановка команд" заменяет только частично .. Почему?
Peter.O
4
IFSне имеет никакого отношения к удалению строк в конце подстановок команд.
Жиль "ТАК - перестань быть злым"