Что не так с "echo $(вещи)" или "эхо` вещи` "?
Я использовал один из следующих
echo $(stuff)
echo `stuff`
(где stuff
например pwd
или же date
или что-то более сложное).
Затем мне сказали, что этот синтаксис неправильный, плохая практика, не элегантный, чрезмерный, избыточный, чрезмерно сложный, программирование культа грузов, нубистский, наивный и т. Д.
Но команда работает, так что именно с ней не так?
2 ответа
ТЛ; др
Подошва stuff
Скорее всего, будет работать для вас.
Полный ответ
Что просходит
Когда ты бежишь foo $(stuff)
Вот что происходит:
stuff
пробеги;- его вывод (stdout) вместо печати заменяет
$(stuff)
в вызовеfoo
; - затем
foo
работает, его аргументы командной строки, очевидно, зависят от того, чтоstuff
вернулся.
это $(…)
Механизм называется "подстановка команд". В вашем случае основная команда echo
который в основном выводит свои аргументы командной строки в стандартный вывод. Так что stuff
пытается распечатать на стандартный вывод захвачен, передан echo
и распечатаны на стандартный вывод echo
,
Если вы хотите вывод stuff
чтобы быть напечатанным на стандартный вывод, просто запустите stuff
,
`…`
синтаксис служит той же цели, что и $(…)
(под тем же именем: "подстановка команд"), хотя есть несколько отличий, поэтому вы не можете слепо поменять их местами. Смотрите этот FAQ и этот вопрос.
Должен ли я избежать 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
имеет эффекты, отличные от записи в стандартный вывод (например, создание временного файла, запуск службы, запуск виртуальной машины, уведомление удаленного сервера), поэтому вы не хотите запускать его несколько раз. - Если
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)"
потому что это не вызывает все другие проблемы, упомянутые здесь.Возможно другой статус выхода (упомянуто для полноты этого вики-ответа; подробности см. В другом ответе, который заслуживает доверия).
Еще одно отличие: код выхода суб-оболочки теряется, поэтому код выхода echo
вместо этого
> stuff() { return 1
}
> stuff; echo $?
1
> echo $(stuff); echo $?
0