Как передать значение переменной в стандартный ввод команды?

105

Я пишу сценарий оболочки, который должен быть в некоторой степени безопасным, т.е. не передавать защищенные данные через параметры команд и, желательно, не использовать временные файлы. Как передать переменную в стандартный ввод команды? Или, если это невозможно, как правильно использовать временные файлы для такой задачи?

Джонатан Леффлер
источник
Может ли злоумышленник измениться $PATH? Так что catможно заменить/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Ответы:

74

Что-то простое:

echo "$blah" | my_cmd
Оливер Чарльзуорт
источник
7
Остерегайтесь пробелов в вашей переменной: echo "$blah"лучше.
Джонатан Леффлер,
13
Посмотрим, как вы справитесь blah=-n, blah=-e... используйте printfвместо этого. unix.stackexchange.com/questions/65803/…
Камило Мартин
1
похоже, это будет хрупким и подверженным ошибкам, не так ли?
ThorSummoner
20
6 лет спустя, если бы я мог удалить этот ответ, я бы (но я не могу, потому что он был принят ...)
Оливер
1
@OliverCharlesworth, почему бы не отредактировать его, чтобы использовать printf '%s\n' "$blah"? С этим изменением (избегая многих ошибок в echo«s спецификации, который отличный ответ Стефан идет в подробно на Почему printfлучше echo? На Unix и Linux , или которые раздел ПРИМЕНЕНИЮ из echoспецификации прикосновений более кратко) это прекрасно разумный ответ.
Чарльз Даффи
195

Передать значение stdinв bash так же просто:

your-command <<< "$your_variable"

Обязательно заключайте в кавычки выражения переменных!

Будьте осторожны, это, вероятно, будет работать только в bashи не будет работать в sh.

Мартин
источник
39
Доступность строк <<<не гарантируется, насколько мне известно, их нет в базе POSIX. Они будут работать, как ожидалось, пока вы их только запускаете bash. Я упоминаю об этом только потому, что OP сказал «оболочка», а не «bash». Хотя он пометил вопрос bash... так что это, очевидно, приемлемый ответ.
JM Becker
Хотя это может быть так, при использовании этого echoметода утечка конфиденциальной информации может быть проще из-за создания канала. Команда herestring будет полностью обработана bash, хотя в этой ситуации (как уже отмечалось) вам лучше знать, что она будет работать на вашем bash, иначе любое сообщение об ошибке, созданное в результате отказа от поддержки herestrings, также приведет к утечке указанной информации.
Стивен Лу
@StevenLu Подождите, echoэто встроенный. Там нет трубы, да? Это Unix, но не может быть так много Unix .
Камило Мартин
4
@StevenLu printf '%s\n' "$var"дает те же результаты, что и, echo "$var"но не сломается, например var=-en, и даже не требует bash.
Camilo Martin
2
Также обратите внимание, что к строке для здесь строк добавляется новая строка.
pdr
19

Обратите внимание, что echo "$var" | commandоперации ' означают, что стандартный ввод ограничен отображаемой строкой (ами). Если вы также хотите, чтобы терминал был подключен, вам нужно быть более изящным:

{ echo "$var"; cat - ; } | command

( echo "$var"; cat -   ) | command

Это означает, что первая строка (строки) будет содержимым, $varа остальная часть будет получена при catчтении стандартного ввода. Если команда не делает ничего особенного (попробуйте включить редактирование командной строки или выполните аналогичные действия vim), тогда все будет в порядке. В противном случае вам нужно по-настоящему фантазировать - я думаю, expectили одна из его производных, вероятно, будет подходящей.

Обозначения командной строки практически идентичны, но вторая точка с запятой необходима с фигурными скобками, тогда как круглые скобки не используются.

Джонатан Леффлер
источник
( echo "$LIST"; cat - ) | sed 1qэто работает для меня, но мне нужно нажать ctrl d, когда я запускаю этот скрипт?
Gert Cuykens
Да; cat -продолжает читать с клавиатуры до EOF или прерываний, так что вы должны сказать ему EOF, набрав управление-D.
Джонатан Леффлер,
есть ли способ обойти EOF? Сэду нужен кот
Герт Куйкенс
Вам вообще не нужно использовать, catесли вам не нужен ввод терминала, как в первой строке. Или вы можете использовать catдля вывода списка файлов. Или ... Если вы хотите, чтобы команда считывала ввод терминала, вы должны сообщить ей, когда она достигла конца ввода. Или вы можете использовать ( echo "$var"; sed /quit/q - ) | command; это продолжается до тех пор, пока вы не наберете строку, содержащую «выйти». Вы можете бесконечно изобретать, как с этим справиться. Остерегайтесь старой городской легенды о программе, которая перестала работать, когда пользователи начали работать с Эквадором. Они набирали название столицы, Кито, и программа закрывалась.
Джонатан Леффлер,
Хорошо, если вы так говорите. Но почему бы и нет echo "$LIST" | sed 1q | ...? Все зависит от того, чем вы занимаетесь. <<EOFОбозначения должны требовать EOF в начале линии на своей собственной где - то файл. Думаю, я бы не стал его использовать, но я все еще не уверен, чего вы пытаетесь достичь. Простой echo "$LIST" | commandили echo $LIST | command, вероятно, будет достаточным на практике (и важно, чтобы вы знали значение разницы между ними).
Джонатан Леффлер,
16

Мне понравился ответ Мартина, но у него есть некоторые проблемы в зависимости от того, что находится в переменной. это

your-command <<< """$your_variable"""

лучше, если ваша переменная содержит "или!"

Роберт Джейкобс
источник
4
Но почему? Также я не могу воспроизвести какие-либо проблемы: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"у меня работает нормально. Что именно делают трое "? AFAIK вы просто добавляете и добавляете пустую строку.
phk
Попробуйте что-нибудь настоящее. Как будто ваша команда - ssh somehost. а ваша переменная - это сценарий оболочки.
Роберт Джейкобс,
16
(cat <<END
$passwd
END
) | command

На catсамом деле не нужен, но он помогает лучше структурировать код и позволяет вам использовать больше команд в круглых скобках в качестве входных данных для вашей команды.

PoltoS
источник
Но этот метод позволяет вам передавать вашей команде несколько строк, а также позволяет использовать пробелы в$passwd
PoltoS
4
На данный момент это лучший ответ, который не приводит к утечке содержимого переменных в конвейер или отслеживание процессов.
user2688272
8

Согласно ответу Мартина, есть функция bash под названием Here Strings (которая сама по себе является вариантом более широко поддерживаемой функции Here Documents ).

http://www.gnu.org/software/bash/manual/bashref.html#Here-Strings

3.6.7 Здесь строки

Вариант представленных здесь документов, формат:

<<< word

Слово раскрывается и передается команде на стандартный ввод.

Обратите внимание, что Here Strings, похоже, предназначены только для bash, поэтому для улучшения переносимости вам, вероятно, будет лучше использовать исходную функцию Here Documents, согласно ответу PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

Или более простой вариант вышеперечисленного:

(cmd <<EOF
$variable
EOF
)

Вы можете опустить (и ), если не хотите, чтобы это перенаправлялось дальше в другие команды.

cnst
источник
5

Этот надежный и портативный способ уже появлялся в комментариях. Это должен быть отдельный ответ.

printf '%s' "$var" | my_cmd

или

printf '%s\n' "$var" | my_cmd

Ноты:

  • Это лучше, чем echo, причины здесь: Почему printfлучше чем echo?
  • printf "$var"неправильно. Первый аргумент формата , где различные последовательности нравится %sили \nкоторые интерпретируются . Чтобы передать переменную правильно, ее нельзя интерпретировать как формат.
  • Обычно переменные не содержат завершающих символов новой строки. Первая команда (с %s) передает переменную как есть. Однако инструменты, работающие с текстом, могут игнорировать или жаловаться на неполную строку (см. Почему текстовые файлы должны оканчиваться новой строкой ? ). Таким образом, вам может понадобиться последняя команда (с %s\n), которая добавляет символ новой строки к содержимому переменной. Неочевидные факты:

    • Здесь строка в Bash ( <<<"$var" my_cmd) добавляет новую строку .
    • Любой метод, который добавляет новую строку, приводит к непустому stdin of my_cmd, даже если переменная пуста или не определена.
Камил Мациоровски
источник
4

Попробуй это:

echo "$variable" | command
unbeli
источник
но не будет ли переменная содержимого $ отображаться, например, в выводе, ps -uкогда выполняется эхо?
2
нет, не будет. echoявляется встроенным, поэтому в ps нет процесса для отображения
unbeli
2
Остерегайтесь пробелов в вашей переменной: echo "$variable"лучше.
Джонатан Леффлер,
верно, bash съест двойные пробелы, более умные оболочки
unbeli
-1

Просто сделать:

printf "$my_var" | my_cmd

Если в переменной var нет пробелов, кавычки можно опустить.
Если вы используете bash, вы также можете:

echo -n "$my_var" | my_cmd

Избегайте использования echo без -n, потому что он будет передавать vraiable с добавленным переносом строки в конце.

Clox
источник
1
не работает, если $ my_var есть %s, printf ничего не печатает. my_var="%s"; printf "$my_var"; - может попробовать printf "%s" "$my_var" | my_cmd ?
hanshenrik