Comment passer la valeur d'une variable au stdin d'une commande?

105

J'écris un script shell qui devrait être un peu sécurisé, c'est-à-dire qui ne transmet pas de données sécurisées via les paramètres des commandes et n'utilise de préférence pas de fichiers temporaires. Comment puis-je passer une variable au stdin d'une commande? Ou, si ce n'est pas possible, comment utiliser correctement les fichiers temporaires pour une telle tâche?

Jonathan Leffler
la source
Un attaquant peut-il changer $PATH? Donc, cela catpeut être remplacé soit/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Réponses:

74

Quelque chose d'aussi simple que:

echo "$blah" | my_cmd
Oliver Charlesworth
la source
7
Méfiez-vous des espaces dans votre variable: echo "$blah"c'est mieux.
Jonathan Leffler
13
Voyons comment vous gérez blah=-n, blah=-e... utilisez à la printfplace. unix.stackexchange.com/questions/65803/…
Camilo Martin
1
cela semble être fragile et sujet aux erreurs, non?
ThorSummoner
20
6 ans plus tard, si je pouvais supprimer cette réponse, je le ferais (mais je ne peux pas car elle a été acceptée ...)
Oliver Charlesworth
1
@OliverCharlesworth, pourquoi ne pas le modifier pour l'utiliser printf '%s\n' "$blah"? Avec ce un changement ( en évitant les nombreux pièges dans echola spécification « , qui excellente réponse de Stéphane va dans en détail Pourquoi printfmieux que echo? Sur Unix et Linux , ou que la section utilisation de l' application des echospécifications touches sur plus brièvement) , il est un parfait réponse raisonnable.
Charles Duffy le
192

Passer une valeur à stdindans bash est aussi simple que:

your-command <<< "$your_variable"

Assurez-vous toujours de mettre des guillemets autour des expressions variables!

Soyez prudent, cela ne fonctionnera probablement que dans bashet ne fonctionnera pas dans sh.

Martin
la source
39
Les Herestrings <<<ne sont pas garantis d'être disponibles, ils ne sont pas dans la base POSIX, pour autant que je sache. Ils fonctionneront comme prévu, tant que vous ne les exécutez que bash. Je le mentionne seulement, car ils OP ont dit «shell» pas spécifiquement «bash». Bien qu'il ait marqué la question avec bash... c'est donc toujours une réponse acceptable.
JM Becker
Bien que cela puisse être le cas, les informations sensibles risquent d'être plus facilement divulguées si vous utilisez la echométhode en raison de la création d'un tuyau. La commande herestring sera entièrement traitée par bash, bien que dans cette situation (comme indiqué) vous feriez mieux de savoir qu'elle fonctionnera sur votre bash, sinon tout message d'erreur produit en raison de la non prise en charge de herestrings entraînerait également une fuite de ces informations.
Steven Lu
@StevenLu Wait, echoest un fichier . Pas de tuyau là-bas, non? C'est Unix, mais ça ne peut pas être autant Unix .
Camilo Martin
4
@StevenLu printf '%s\n' "$var"produit les mêmes résultats que echo "$var"mais ne cassera pas si, par exemple var=-en, et ne nécessite même pas de bash.
Camilo Martin
1
Notez également qu'une nouvelle ligne est ajoutée à la chaîne pour les chaînes ici.
pdr
19

Notez que les echo "$var" | commandopérations ' signifient que l'entrée standard est limitée à la (aux) ligne (s) renvoyée (s). Si vous souhaitez également que le terminal soit connecté, vous devrez être plus sophistiqué:

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

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

Cela signifie que la ou les première (s) ligne (s) seront le contenu de $varmais le reste viendra de la catlecture de son entrée standard. Si la commande ne fait rien de trop compliqué (essayez d'activer l'édition de la ligne de commande ou exécutez comme le vimfait), tout ira bien. Sinon, vous devez être vraiment sophistiqué - je pense que expectou l'un de ses dérivés est susceptible d'être approprié.

Les notations de la ligne de commande sont pratiquement identiques - mais le deuxième point-virgule est nécessaire avec les accolades alors qu'il ne l'est pas entre parenthèses.

Jonathan Leffler
la source
( echo "$LIST"; cat - ) | sed 1qcela fonctionne pour moi mais je dois appuyer sur ctrl d lorsque j'exécute ce script?
Gert Cuykens
Oui; le cat -continue à lire sur le clavier jusqu'à EOF ou interrompre, vous devez donc lui dire EOF en tapant control-D.
Jonathan Leffler
y a-t-il un moyen de contourner EOF? sed a besoin d'un chat
Gert Cuykens
Vous n'avez pas à utiliser catdu tout si vous ne voulez pas d'entrée de terminal, comme dans la première ligne. Ou vous pouvez utiliser catpour lister un fichier. Ou ... Si vous voulez que la commande lise l'entrée du terminal, vous devez lui dire quand elle a atteint la fin de l'entrée. Ou vous pouvez utiliser ( echo "$var"; sed /quit/q - ) | command; cela continue jusqu'à ce que vous tapiez une ligne contenant «quitter». Vous pouvez être infiniment inventif avec la façon dont vous le gérez. Méfiez-vous de la vieille légende urbaine d'un programme qui a cessé de fonctionner lorsque les utilisateurs ont commencé à travailler avec l'Équateur. Ils tapaient le nom de la capitale, Quito, et le programme se fermait.
Jonathan Leffler
OK si vous le dites. Mais pourquoi pas simplement echo "$LIST" | sed 1q | ...? Tout dépend de ce que vous faites. La <<EOFnotation devrait exiger un EOF au début d'une ligne seule quelque part dans le fichier. Je ne l'utiliserais pas, je pense - mais je ne suis toujours pas sûr de ce que vous essayez d'accomplir. Un simple echo "$LIST" | commandou echo $LIST | commandsuffira probablement en pratique (et il est important que vous connaissiez la signification de la différence entre les deux).
Jonathan Leffler
16

J'ai aimé la réponse de Martin, mais elle a quelques problèmes en fonction du contenu de la variable. Ce

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

est mieux si votre variable contient "ou!

Robert Jacobs
la source
4
Mais pourquoi? De plus, je ne peux reproduire aucun problème: cela foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"fonctionne très bien pour moi. Que font exactement les trois "? AFAIK, vous êtes juste en train de préfixer et d'ajouter une chaîne vide.
phk
Essayez quelque chose de réel. Comme votre commande est ssh somehost. et votre variable est un script shell.
Robert Jacobs
16
(cat <<END
$passwd
END
) | command

Le catn'est pas vraiment nécessaire, mais il aide à mieux structurer le code et vous permet d'utiliser plus de commandes entre parenthèses comme entrée de votre commande.

PoltoS
la source
Mais cette méthode vous permet de passer plusieurs lignes à votre commande et autorise également les espaces dans le$passwd
PoltoS
4
C'est la meilleure réponse à ce jour qui ne laisse pas échapper le contenu variable à la canalisation ou au processus de surveillance.
user2688272
8

Selon la réponse de Martin, il existe une fonctionnalité bash appelée Here Strings (qui est elle-même une variante de la fonctionnalité Here Documents plus largement prise en charge ).

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

3.6.7 Chaînes Here

Variante des documents ici, le format est:

<<< word

Le mot est développé et fourni à la commande sur son entrée standard.

Notez que Here Strings semble être uniquement bash, donc, pour une meilleure portabilité, vous feriez probablement mieux d'utiliser la fonction originale Here Documents, selon la réponse de PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

Ou, une variante plus simple de ce qui précède:

(cmd <<EOF
$variable
EOF
)

Vous pouvez omettre (et ), sauf si vous souhaitez que cela soit redirigé vers d'autres commandes.

cnst
la source
5

Cette méthode robuste et portable est déjà apparue dans les commentaires. Ce devrait être une réponse autonome.

printf '%s' "$var" | my_cmd

ou

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

Remarques:

  • C'est mieux que echo, les raisons sont ici: pourquoi vaut printfmieux que echo?
  • printf "$var"est faux. Le premier argument est le format où diverses séquences aiment %sou \nsont interprétées . Pour passer la variable à droite, elle ne doit pas être interprétée comme format.
  • Les variables ne contiennent généralement pas de retours à la ligne finaux. L'ancienne commande (avec %s) transmet la variable telle quelle. Cependant, les outils qui fonctionnent avec du texte peuvent ignorer ou se plaindre d'une ligne incomplète (voir Pourquoi les fichiers texte devraient-ils se terminer par une nouvelle ligne? ). Vous pouvez donc souhaiter la dernière commande (avec %s\n) qui ajoute un caractère de nouvelle ligne au contenu de la variable. Faits non évidents:

    • Ici, la chaîne dans Bash ( <<<"$var" my_cmd) ajoute une nouvelle ligne.
    • Toute méthode qui ajoute une nouvelle ligne entraîne un stdin non vide de my_cmd, même si la variable est vide ou indéfinie.
Kamil Maciorowski
la source
4

Essaye ça:

echo "$variable" | command
unbeli
la source
mais la variable contents $ n'apparaîtrait-elle pas par exemple dans la sortie de ps -uquand echo est en cours d'exécution?
2
non, ce ne sera pas le cas. echoest un intégré, il n'y a donc aucun processus à afficher dans ps
unbeli
2
Méfiez-vous des espaces dans votre variable: echo "$variable"c'est mieux.
Jonathan Leffler
c'est vrai, bash mangera des espaces doubles, les coquilles plus intelligentes ne le feront pas
unbeli
-1

Faites simplement:

printf "$my_var" | my_cmd

Si la variable ne contient pas d'espaces, les guillemets peuvent être omis.
Si vous utilisez bash, vous pouvez également faire:

echo -n "$my_var" | my_cmd

Évitez d'utiliser echo sans -n car il redirigera la vraiable avec un saut de ligne ajouté à la fin.

Clox
la source
1
ne fonctionne pas si $ my_var est %s, printf n'imprimera rien. my_var="%s"; printf "$my_var"; - peut-être essayer printf "%s" "$my_var" | my_cmd ?
hanshenrik