Куди пішов останній знак нового рядка від заміни моєї команди?


15

Наступний код найкраще описує ситуацію. Чому останній рядок не виводить кінцевий знак нового рядка? Вихід кожного рядка відображається в коментарі. Я використовую GNU bash, версія 4.1.5

     echo -n $'a\nb\n'                  | xxd -p  # 610a620a  
           x=$'a\nb\n'   ; echo -n "$x" | xxd -p  # 610a620a
     echo -ne "a\nb\n"                  | xxd -p  # 610a620a
x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p  # 610a62

4
Обхід для рідкісних часів, коли це потрібно:tmp=$(somecommand; echo a); tmp=${tmp%a}
Жил 'SO- перестань бути злим'


1
@Gilles: Перегляньте свій приклад вище: tmp=$(somecommand; echo a)... Це, безумовно, призвело до домівки до моменту ... Поки я не побачив приклад, мою схильність все одно було б використовувати echo -n a... але, звичайно ж, немає необхідності тому -n, що Command Substitution видалить введений кінцевий новий рядок у будь-якому випадку! ... дякую ...
Peter.O

Відповіді:


18

Функція підстановки команд $()(і її двоюрідний брат backtick) спеціально видаляє зворотні нові рядки. Це документально підтверджена поведінка , і ви завжди повинні знати про це, використовуючи конструкцію.

Нові рядки всередині тексту тексту оператором підстановки не видаляються, але вони також можуть бути видалені під час розбиття слів на оболонці, тож як це вийде, залежить від того, використовували ви лапки чи ні. Зверніть увагу на різницю між цими двома звичаями:

$ echo -n "$(echo -n 'a\nb')"
a
b

$ !! | xxd -p
610a62

$ echo -n  $(echo -n 'a\nb')
a b

$ !! | xxd -p   
612062

У другому прикладі результат не цитується, а новий рядок трактується як розбиття слів, завдяки чому він відображається у висновку як пробіл!


1
Дякую Калебу .. Мені було відомо, що розділення пробілів на слово змінюється на єдиний пробіл, коли його не цитують ... Ось чому я найбільше здивувався, побачивши, як мій трейлінг нового рядка зник, хоча я його процитував ... Тепер я знаю, що це тому, що "нормальна" поведінка Command Substitution викидає кінцевий новий рядок ... Ну добре, c'est la vie .. і дякую за посилання
Peter.O

6

При використанні підстановки команд оболонка виконує команди в підшалі, повертаючи їх stdout. в цьому процесі символи IFS втрачають свою значимість (якщо вони не цитуються), оскільки команда повертає прості розбиті слова, тож залишкові видаляються. Наприклад:

$ echo "$(echo -e '\n')" | wc
 1       0       1

$ echo -e '\n' | wc
2       0       2

і, практично, він pwdбуде працювати, навіть якщо ім'я вашого каталогу має новий рядок між ними, але $(pwd)не буде.

Звичайний спосіб вирішення - додати щось наприкінці своєї команди та знімати його після цього.


1
Завдяки Philomath ... до речі, ви перший приклад синтаксично неправильно ( «туалет» не приймає рядок в якості арг) .. Для вас приклади , щоб зрозуміти, перший може бути echo "$(echo -e '\n')" | wc, якісь виходи 1   0   1, по порівнянні з2   0   2
Пітер.О

@fred: На жаль, просто помилка друку.
Філомат

1
"перетворене в пробіли" не є відповідним поясненням, є лише деякий розкол. Зокрема, ви можете видалити простір з IFS, і вони не будуть діяти як роздільники. Більше того, ваш приклад потрапляє до категорії спеціальних випадків, і є різниця між $(pwd)і "$(pwd)", дивіться відповідь Калеба.
Stéphane Gimenez

PS .. Ваша пропозиція про звичайний спосіб вирішення - це саме те, що мені потрібно було очистити.
Peter.O

Повторно "перетворений на пробіли", див. Розщеплення слів
Peter.O
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.