Чому оболонка Command Substitution заміщує останній знак нового рядка?


25

Як показано в наступному прикладі, і як у моєму недавньому запитанні В баші, куди пішов останній знак нового рядка? , Я хочу знати "чому" це відбувається

  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

Я припускаю, що мають бути деякі для дії оболонки дуже вагома причина, а саме «Заміна команди», щоб фактично видалити деякі дані з виводу команди, яку вона замінює ...
але я не можу обернути голову навколо цього, як це здається антитеза того, що він повинен робити .. тобто. щоб повернути результат команди назад в процес скрипту ... Затримка одного символу мені здається дивним, але я вважаю, що для цього є розумна причина ... Я хочу дізнатися, що це за причина. .


Відповіді:


22

Тому що спочатку оболонка не мала бути повноцінною мовою програмування.

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

Тож, можливо, ви приймете це питання як відповідь:

Чи можете ви знайти простий спосіб видалити трейлінг, \nякщо це не було зроблено автоматично в наступній команді?

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

Зауважте, що цитування потрібно для запобігання розбиття подвійних пробілів, які можуть відображатися у форматованих датах.


1
Якщо те, що ви говорите, є правдою, то я був би абсолютно приголомшений. Якщо є shoptможливість змінити описане вами поведінку за замовчуванням, то я б знову був щасливий ... Мені важко думати, що Bash / Linux / Unix забруднить таку критичну функцію, як "захоплення stdout" тільки тому, dateщо не має опція з / без '\ n' ... Очевидним виправленням буде те, що баш (або інша оболонка) має shoptдля цього ... Чи знаєте ви про що? .. а чи є у вас якісь посилання, які говорять про те, про що йдеться у цій справі? ... і чому немає date\ n опції. Це повинно!
Пітер.О

2
Заміни команд з'явилися в першій версії оболонки Борна у "7-му виданні" Unix у 1979 році. Це вже була велика революція, і я думаю (я не народився), що ніхто не піклувався про те, щоб зберегти "\ n" чи ні. Так, ви можете прочитати manpage менш ніж за 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

Ваше "запитання" про дату було найсильнішим підказом до мого розуміння "чому" командний субститут робить те, що робить, тому це було "найкращою" відповіддю на моє запитання ... і мені, звичайно, потрібні були й інші хороші відповіді. ..
Пітер.О

19

Це частина стандарту :

Оболонка повинна розширити підстановку команд, виконавши команду в середовищі підрозділу (див. Середовище виконання оболонки ) та замінивши заміну команди (текст команди плюс додається "$ ()" або зворотні котирування) на стандартний вихід команди, видаляючи послідовності одного або декількох <newline> s в кінці заміщення.

Звичайно, стандарт, ймовірно, написаний таким чином, тому що це kshбуло так чи щось, але це стандарт, і це документально підтверджена поведінка. Якщо вам це не подобається, використовуйте Perl або щось інше, що дозволить вам зберігати останні рядки.


Приємний підсумок .. Дякую .. і посилання, безумовно, допомогли
Peter.O

4
Стандарт такий, тому що так робила оболонка Борна і будь-яка інша оболонка.
Жиль 'ТАК - перестань бути злим'

6

Ну, для мене це має сенс. Новий рядок є лише в першу чергу, у звичайному виведенні команди, так що підказка з'являється після завершення команди в новому рядку. Новий рядок у більшості випадків не є частиною оригінального виходу, він є для пристосування екрана. Коли ви аналізуєте вихід з команди, новий рядок, як правило, є проблемою. Чому wcкоманда виводить 2 рядки тексту? О, це не так, він виводить один із наступним рядком. Коли ти розбираєшсяwc ви не хочете турбуватися про те, що є 2 рядки виводу - насправді немає, є лише один.


@EightBitTony: Моє запитання жодним чином не пов'язане із тим, щоб щось відображати в терміналі. Це зовсім прямо. Якщо є новий рядок для відображення, він буде відображати новий рядок. Тут йдеться про те, що \nв початковому stdoutпотоці видаляється командна заміна. тобто. мої оригінальні дані (дуже важливі дані) видалено останні рядки, навіть коли дані загорнуті в "лапки". Я досить зрозуміти , як і чому працює Слово Нарізка, але я не маю ні найменшого поняття , чому Підстановка команд будуть віддалятися тільки нові рядки і тільки хвостові з них.
Пітер.О

1
Ніщо, що ви сказали, не змінює мою думку. Частіше за все остаточний рядок у будь-якому виході є виключно для відображення, а не як частина даних.
EightBitTony

Я погоджуюся з вашим поглядом, і все разом ... Так, новий рядок в кінці виходу є для зручності ... але моя проблема не має нічого спільного з цим ... Я запитую: Чому Заміна командування насильно видаляє її ? ... Це два різні питання .. Можливо, моє питання має бути: чи існує вбудований спосіб вирішення проблеми? . тому що необхідність кодувати цю проблему, додаючи слідуючий манекен, значить! ... Я зроблю це, якщо мені доведеться, але це перший раз, коли мене змусив баш (і я в шоковому стані :) .. Це так добре так ...
Пітер.О

Як вже згадувалося в іншій відповіді, це частина стандарту. Для мене я вважаю, що це робиться саме так, оскільки при опрацюванні даних ви хочете бачити лише дані, а не зручний новий рядок. Це тому, що оболонка очікує, що ви будете обробляти дані в трубі, а нова лінія не є даними. Більше, і нам потрібно взяти це на обговорення.
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

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 заміни - це просто звичайне виконання commen в підколонці, тому воно слідує тій же логіці.


@Philomath: Обидва x="hello  "і x="hello     "втратять усі свої пробіли, якщо відображатимуться через echo $x... Однак втрачається лише останній новий рядок командного заміну.
Пітер.О

дивно, я не бачу різниці в моєму ударі (підтверджуючи xxd)
Філомат

Я просто перевіряю це ... Це видаляє всі останні нові рядки ... Я експериментував із IFS, коли в мене склалося таке враження, тож давайте скасувати це :) .. але питання все ще залишається ... Це фундаментальна частина розбиття слів конденсуватися кілька прогалин в єдиному просторі, і повністю видалити початкові і кінцеві пробіли .. я можу зрозуміти, але я , звичайно , не розумію , чому, з Підстановка команд, це тільки смуги \ п і не всі прогалини (як з розділенням слів), а чому лише кінцевий кінець? .. І перш за все: "Заміна команди" лише частково заміщає .. Чому?
Пітер.О

4
IFSне має нічого спільного з зачисткою нових рядків в кінці підстановок команд.
Жил 'ТАК - перестань бути злим'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.