Pseudo-fichiers pour données temporaires

98

Je veux souvent alimenter des données en chaîne relativement courtes (éventuellement plusieurs lignes) à des programmes en ligne de commande n'acceptant que les entrées de fichiers (par exemple, wdiff) de manière répétée. Bien sûr, je peux créer un ou plusieurs fichiers temporaires, enregistrer la chaîne à cet emplacement et exécuter la commande avec le nom du fichier en tant que paramètre. Mais il me semble que cette procédure serait très inefficace si les données sont réellement écrites sur le disque et que cela pourrait également endommager le disque plus que nécessaire si je répète cette procédure plusieurs fois, par exemple si je veux alimenter des lignes simples de texte long fichiers à wdiff. Existe-t-il un moyen recommandé de contourner ce problème, par exemple en utilisant des pseudo-fichiers tels que des tubes pour stocker temporairement les données sans les écrire sur le disque (ou en les écrivant uniquement si elles dépassent une longueur critique). Notez que wdiff prend deux arguments et,wdiff <"text".

highsciguy
la source
Cela peut-il être résolu via xargs?
NN
Je ne sais pas, mais ce ne serait pas évident pour moi de savoir comment. Autant que je sache xargs, les lignes d'entrée seraient définies à partir des arguments de chaîne de fichier de la commande. Mais j'ai besoin du contraire.
highsciguy
@rahmu J'ai jeté un coup d'œil, mais je pense que le problème est un peu différent. Au moins, je ne vois pas comment les réponses pourraient aider. La réponse acceptée pour produire correctement des fichiers temporaires est essentiellement ce que je ne voudrais pas éviter, sinon il existe une sorte de mise en mémoire tampon qui empêche en réalité l'écriture des fichiers. J'ai une compréhension limitée du fonctionnement des fichiers temporaires!
highsciguy
Quel est le problème avec echo $data_are_here | dumb_program?
vonbrand
1
Cela ne prend en charge qu'un seul fichier d'entrée et tous les programmes ne liraient pas à partir de stdin.
highsciguy

Réponses:

55

Utilisez un tuyau nommé . À titre d'illustration:

mkfifo fifo
echo -e "hello world\nnext line\nline 3" > fifo

Le -edit à l'écho d'interpréter correctement la nouvelle ligne d'échappement ( \n). Cela bloquera, c'est-à-dire que votre shell sera suspendu jusqu'à ce que quelque chose lise les données du canal.

Ouvrez un autre shell quelque part et dans le même répertoire:

cat fifo

Vous allez lire l'écho, ce qui libérera l'autre shell. Bien que le canal existe en tant que noeud de fichier sur le disque, les données qui le traversent ne le sont pas; tout se passe en mémoire. Vous pouvez fond ( &) l'écho.

Le tube a un tampon de 64 k (sous Linux) et, comme un socket, bloquera l’écrivain quand il sera plein, ainsi vous ne perdrez pas de données tant que vous ne le tuez pas prématurément.

boucle d'or
la source
Ok, merci, cela fonctionne aussi avec deux pipes nommées et wdiff. Mais je pensais comprendre qu’une certaine (petite) quantité de mémoire était disponible pour le tuyau en tant que tampon. Que se passe-t-il si je dépasse la taille de la mémoire tampon?
highsciguy
J'ai ajouté un dernier paragraphe sur cette question.
goldilocks
3
/tmpest configuré dans la plupart des distributions pour utiliser un tmpfssystème de fichiers en RAM. Lorsque vous écrivez un fichier, /tmpil se connecte directement à votre RAM, ce qui en fait une bonne réponse pour les fichiers semi-résilients auxquels il faut accéder rapidement et être réécrits à plusieurs reprises.
130

Dans Bash, vous pouvez utiliser la command1 <( command0 )syntaxe de redirection, qui redirige command0le stdout et le transmet à un command1qui prend un nom de fichier comme argument de ligne de commande. C'est ce qu'on appelle la substitution de processus .

Certains programmes utilisant des arguments de ligne de commande de nom de fichier nécessitent en réalité un véritable fichier à accès aléatoire. Par conséquent, cette technique ne fonctionnera pas pour ceux-ci. Cependant, cela fonctionne bien avec wdiff:

user@host:/path$ wdiff <( echo hello; echo hello1 ) <( echo hello; echo hello2 )
hello
[-hello1-]
{+hello2+}

En arrière-plan, cela crée une FIFO, dirige la commande à l'intérieur <( )de la FIFO et transmet le descripteur de fichier de la FIFO en tant qu'argument. Pour voir ce qui se passe, essayez de l’utiliser avec echopour imprimer l’argument sans rien faire:

user@host:/path$ echo <( echo hello )
/dev/fd/63

La création d'un canal nommé est plus flexible (si vous souhaitez écrire une logique de redirection compliquée à l'aide de plusieurs processus), mais pour de nombreux objectifs, cela suffit et est évidemment plus facile à utiliser.

Il y a aussi la >( )syntaxe pour quand vous voulez l'utiliser en sortie, par exemple

$ someprogram --logfile >( gzip > out.log.gz )

Voir aussi la feuille de triche pour les redirections Bash pour les techniques associées.

Escargot mécanique
la source
Ceci n'est pas supporté dans KSH
chanchal1987
5
ksh a inventé cela. Vous utilisez une variante de ksh qui ne le supporte pas
Neil McGuigan le
2
Certains programmes utilisant des arguments de ligne de commande de nom de fichier nécessitent en réalité un véritable fichier à accès aléatoire. Par conséquent, cette technique ne fonctionnera pas pour ceux-ci. Que faites-vous dans ces cas? Par exemple ssh -F <(vagrant ssh-config) defaultserait vraiment sympa mais hélas.
Sukima
10

wdiff est un cas particulier car il nécessite 2 arguments de nom de fichier, mais pour toutes les commandes ne nécessitant qu'un argument et qui refusent obstinément de prendre autre chose qu'un argument de nom de fichier, il existe 2 options:

  • Le nom de fichier '-' (c'est-à-dire un signe moins) fonctionne environ la moitié du temps. Cela semble dépendre de la commande en question et de la question de savoir si le développeur de la commande intercepte ce cas et le gère comme prévu. par exemple

    $> ls | chat -

  • Il existe un fichier psuedo nommé / dev / stdin qui existe sous Linux et qui peut être utilisé lorsqu'un nom de fichier est absolument requis par une commande. Ceci est plus susceptible de fonctionner car il ne nécessite aucune manipulation de nom de fichier spéciale à partir de la commande. Si un fifo fonctionne ou si la méthode de substitution du processus bash fonctionne, cela devrait également fonctionner et n'est pas spécifique au shell. par exemple

    $> ls | chat / dev / stdin

Dabuntu
la source
1
moins et openssl comme / dev / stdin plutôt que / dev / fd / NUM :-)
eel ghEEz