Різниця між 'ls' та 'echo $ (ls)'


27

Розглянемо два зразки оболонок

$ ls
myDoc.html
SomeDirectory
someDoc.txt

і

$ echo $(ls)
myDoc.html SomeDirectory someDoc.txt

Перший виконує, lsякий, наскільки я розумію, додає вміст поточної робочої директорії до stdoutфайлу (що і показує термінал). Це правильно?

Друга приймає значення lsкоманди (що означає вміст поточної робочої директорії) і друкує її у stdoutфайл. Це правильно?

Чому дві команди дають різні виходи?


1
бо ти робиш відлуння. Вихід ls як вхід в команду echo.
ankidaemon

Це "очікувана" поведінка, яку хтось коротко пояснить. Однак ви впевнені, що lsсам по собі не дає вам декілька елементів на рядок?
roaima

@roaima columnized ls - це залишок bsd "функції". Універсальні версії друкують по одному запису на рядок
Стів Кокс

@SteveCox Я не користувався належним UNIX з раннього SVR4, тому моя пам’ять трохи туманна. Дякую за нагадування.
roaima

Чому ні echo *?
jdh8

Відповіді:


70

Під час виконання цієї команди:

ls

термінал відображає вихід ls.

Під час виконання цієї команди:

echo $(ls)

оболонка фіксує вихід $(ls)і виконує розділення слів на ньому. За замовчуванням IFSце означає, що всі послідовності пробілу, включаючи символи нового рядка, замінені одним порожнім. Ось чому результат echo $(ls)з'являється в одному рядку.

Для розширеного обговорення розділення слів див . Поширені запитання Грега .

Придушення поділу слова

Оболонка не виконує розбиття слів на рядки в лапках. Таким чином, ви можете придушити поділ слів і зберегти багаторядковий вихід за допомогою:

echo "$(ls)"

ls і багаторядний вихід

Можливо, ви помітили, що lsіноді друкується більше одного файлу в рядку:

$ ls
file1  file2  file3  file4  file5  file6

Це за замовчуванням, коли вихід lsпереходить до терміналу. Коли вихід не збирається безпосередньо до терміналу, lsзмінює його за замовчуванням на один файл у рядку:

$ echo "$(ls)"
file1
file2
file3
file4
file5
file6

Така поведінка задокументована в man ls.

Ще одна тонкість: підміна команд і затримка нових рядків

$(...)- це підміна команд, і оболонка видаляє сліди нового рядка з виведення підстановки команд . Зазвичай це не помітно, оскільки за замовчуванням echoдо кінця виводу додається один новий рядок. Отже, якщо ви втратите один новий рядок з кінця $(...)і ви отримаєте його echo, зміни не буде. Якщо, однак, результат вашої команди закінчується двома або більше символами нового рядка, при цьому echoдодається лише один, у вашому результаті буде відсутні один або більше нових рядків. Як приклад, ми можемо використовувати printfдля генерування символів у новому рядку. Зауважте, що обидві наступні команди, незважаючи на різну кількість нових рядків, дають однаковий вихід одного порожнього рядка:

$ echo "$(printf "\n")"

$ echo "$(printf "\n\n\n\n\n")"

$ 

Така поведінка задокументована в man bash.

Ще один сюрприз: розширення імені шляху, вдвічі

Створимо три файли:

$ touch 'file?' file1 file2

Зверніть увагу на різницю між ls file?та echo $(ls file?):

$ ls file?
file?  file1  file2
$ echo $(ls file?)
file? file1 file2 file1 file2

У випадку з echo $(ls file?)файлом глобус file?розширюється вдвічі , внаслідок чого імена файлів file1і file2двічі з’являються у висновку. Це тому, що, як зазначає Джеффікінс, розширення імені шляху здійснюється спочатку оболонкою перед lsзапуском, а потім ще раз, перш ніж echoзапускається.

Друге розширення шляху може бути припинено , якщо ми використовували подвійні лапки:

$ echo "$(ls file?)"
file?
file1
file2

4
Крім того, за допомогою isattyви можете перевірити, чи stdout - це tty, ls використовує це для визначення того, використовувати кольори чи ні.
Янус Троельсен

1
Чи не відповідають котирування в останньому фрагменті коду? Або $( )насправді забезпечують синтаксичний бар'єр, щоб вам не потрібно інтерполювати?
кіт

6
@cat $()забезпечує синтаксичний бар'єр.
Випадково832

2
Ще одна важлива відмінність: якщо будь-яке ім’я файлу містить символ підстановки, echoкоманда розширить підстановку . Наприклад, якщо lsповертає файл? file1 file2 , то echo $(ls)поверне файл? file1 file2 file1 file2 .
Джеффікінс

1
@Jeffiekins echoне розширює шаблони; оболонка робить, перш ніж передавати отримане розширення як аргументи echo.
Чепнер

0

ls знає, надсилає він висновок в термінал чи ні.

Виконання lsв командному рядку запише в вашу псевдо-tty. Перенаправлення виводу з lsпрограми, як правило, не збирається псевдо-tty, а lsформатування результатів буде по-іншому.

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