тл; д-р
Підошва stuff
, швидше за все, працює для вас.
Повна відповідь
Що сталося
Коли ви запускаєтесь foo $(stuff)
, це відбувається так:
stuff
пробіжки;
- його вихід (stdout) замість друку замінює
$(stuff)
на виклик foo
;
- потім
foo
запускається, аргументи його командного рядка очевидно залежать від того, що stuff
повернулося.
Цей $(…)
механізм називається "підміна команд". У вашому випадку головна команда - echo
це в основному друкує аргументи командного рядка до stdout. Тож те, що stuff
намагається надрукувати в stdout, захоплюється, передається echo
та друкується до stdout echo
.
Якщо ви хочете, щоб вихід stuff
був надрукований у stdout, просто запустіть підошву stuff
.
`…`
Синтаксис служить ті ж цілям, що і $(…)
(під той же назвою: «команда заміна»), є кілька відмінностей , хоча, так що ви не можете сліпо поміняти їх. Дивіться це поширене запитання та це запитання .
Чи слід уникати, echo $(stuff)
незважаючи ні на що?
Є причина, яку ви можете використовувати, echo $(stuff)
якщо ви знаєте, що робите. З тієї ж причини вам слід уникати, echo $(stuff)
якщо ви насправді не знаєте, що робите.
Справа є stuff
і echo $(stuff)
не є рівнозначною. Останнє означає викликати оператор split + glob на виході stuff
з значенням за замовчуванням $IFS
. Подвійне цитування підстановки команд перешкоджає цьому. Одноразове цитування підстановки команд змушує її більше не бути заміною команди.
Щоб спостерігати за цим, коли мова заходить про розщеплення, виконайте ці команди:
echo "a b"
echo $(echo "a b")
echo "$(echo "a b")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "a b")'
А для глобалізації:
echo "/*"
echo $(echo "/*")
echo "$(echo "/*")" # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'
Як бачите echo "$(stuff)"
, еквівалент (-ish *) stuff
. Ви можете використовувати його, але який сенс ускладнювати речі таким чином?
З іншого боку, якщо ви хочете, щоб результат stuff
піддався розщепленню + глобулювання, то ви можете виявитись echo $(stuff)
корисним. Але це має бути вашим свідомим рішенням.
Є команди, що генерують вихід, які слід оцінювати (що включає розщеплення, глобулювання тощо) та запускати оболонку, тому eval "$(stuff)"
є можливість (див. Цю відповідь ). Я ніколи не бачив команди, яка потребує її виводу, щоб пройти додаткове розщеплення + глобулювання перед друком . Навмисне використання echo $(stuff)
здається дуже рідкісним.
Про що var=$(stuff); echo "$var"
?
Влучне зауваження. Цей фрагмент:
var=$(stuff)
echo "$var"
має бути еквівалентним echo "$(stuff)"
(-ish *) stuff
. Якщо це весь код, просто запустіть stuff
замість цього.
Якщо, однак, вам потрібно використовувати результати stuff
більше, ніж один раз, тоді цей підхід
var=$(stuff)
foo "$var"
bar "$var"
зазвичай краще, ніж
foo "$(stuff)"
bar "$(stuff)"
Навіть , якщо foo
це echo
і ви отримаєте echo "$var"
в вашому коді, це може бути краще , щоб зберегти його таким чином. Що слід врахувати:
- З
var=$(stuff)
stuff
пробіжками один раз; навіть якщо команда швидка, уникати обчислення одного і того ж результату двічі - це правильна річ. Або, можливо, stuff
є інші ефекти, крім запису до stdout (наприклад, створення тимчасового файлу, запуск послуги, запуск віртуальної машини, повідомлення про віддалений сервер), тому ви не хочете запускати його кілька разів.
- Якщо
stuff
генерується залежний від часу або дещо випадковий вихід, ви можете отримати непослідовні результати від foo "$(stuff)"
та bar "$(stuff)"
. Після того, var=$(stuff)
як значення $var
буде зафіксовано, ви можете бути впевнені foo "$var"
і bar "$var"
отримати ідентичний аргумент командного рядка.
У деяких випадках замість foo "$var"
вас, можливо, потрібно (потрібно) використовувати foo $var
, особливо якщо stuff
генерує кілька аргументів для foo
(змінна масиву може бути кращою, якщо ваша оболонка підтримує її). Знову ж таки, знайте, що ви робите. Коли мова йде echo
про різницю між echo $var
і echo "$var"
є такою ж, як між echo $(stuff)
і echo "$(stuff)"
.
* Еквівалент ( -іш )?
Я сказав echo "$(stuff)"
, що рівнозначно (-ish) до stuff
. Є щонайменше два питання, які роблять це не зовсім рівнозначним:
$(stuff)
працює stuff
в нижній частині, тому краще сказати echo "$(stuff)"
, що еквівалентно (-ish) (stuff)
. Команди, які впливають на оболонку, в яку вони запускаються, якщо вони знаходяться в нижній частині, не впливають на основну оболонку.
У цьому прикладі stuff
є a=1; echo "$a"
:
a=0
echo "$(a=1; echo "$a")" # echo "$(stuff)"
echo "$a"
Порівняйте його з
a=0
a=1; echo "$a" # stuff
echo "$a"
і с
a=0
(a=1; echo "$a") # (stuff)
echo "$a"
Інший приклад, почніть з stuff
того, щоб бути cd /; pwd
:
cd /bin
echo "$(cd /; pwd)" # echo "$(stuff)"
pwd
і тест stuff
та (stuff)
версії.
echo
не є хорошим інструментом для відображення неконтрольованих даних . Це echo "$var"
ми повинні були говорити printf '%s\n' "$var"
. Але оскільки питання згадується echo
і оскільки найімовірніше рішення - це використовувати не echo
в першу чергу, я вирішив не вводити printf
до цього часу.
stuff
або (stuff)
буде перемежовувати вивід stdout та stderr, при цьому echo $(stuff)
буде надруковано весь висновок stderr з stuff
(який запускається спочатку), і лише тоді вихід висновку stdout засвоюється echo
(який працює останнім).
$(…)
знімає будь-який echo
зворотний рядок, а потім додає його назад. Таким чином , echo "$(printf %s 'a')" | xxd
дає інший результат , ніж printf %s 'a' | xxd
.
Деякі команди ( ls
наприклад) працюють по-різному, залежно від того, стандартний вихід - консоль чи ні; так ls | cat
не робить те ж саме ls
. Аналогічно echo $(ls)
буде працювати інакше, ніж ls
.
Введення в ls
сторону, в загальному випадку , якщо у вас є , щоб змусити то це інша поведінка stuff | cat
краще echo $(ls)
або echo "$(ls)"
тому , що він не викликає всі інші питання , згадані тут.
Можливо, інший статус виходу (згаданий для повноти відповіді у вікі; для детальної інформації див. Іншу відповідь, яка заслуговує на зарахування).