10
votes

Meilleure façon de modifier un fichier lors de l'utilisation de tuyaux?

J'ai souvent des tâches de programmation shell où je rencontre ce modèle:

cat file | bufferUntilEOF | script > file


7 commentaires

Euh, Xargs devrait faire l'affaire, non?


Je ne crois pas. Eh bien, peut-être que c'est le cas, mais sa documentation indique que le problème que la résolution de ses effets est la manipulation des cas où la limite de l'argument de commande est exayérée. Il ne dit pas que cela tamponne tout de stdin avant d'ouvrir stdout.


Je pense qu'il existe des options à Xargs qui traitent de la taille de la mémoire tampon.


BTW, la réduction de toute l'entrée pourrait conduire à un SEGV ou à un sigbus en raison d'une mémoire hors de mémoire.


C'est possible. Mais supposons que je ne fais que traiter des fichiers de code source une à la fois, dont beaucoup peuvent dépasser la taille du tampon interprocessé (généralement 65536 octets), et mon ordinateur dispose de plusieurs gigaoctets de mémoire disponibles.


L'écriture dans un fichier temporaire n'est pas un peu plus lente que d'écrire sur un fichier existant, vous utiliserez le double de l'espace disque. Déplacement du fichier Temp Back est juste une simple renommée, pas une copie aussi longtemps que sur le même point de montage.


Unix.stackexchange.com/questions/11067/...


9 Réponses :


1
votes

Utiliser un fichier temporaire est meilleur que d'essayer de tamponner les données dans le pipeline.

Il défait presque le but des pipelines de les tamponner.


5 commentaires

Eh bien, peut-être. Cela ressemble à un argument religieux cependant. Je sais que tous les fichiers sont facilement adaptés dans une minuscule partie de la mémoire principale (mon script shell fonctionnera sur chaque fichier source dans un très grand référentiel SVN). Le fichier temporaire fera courir deux fois plus tard que nécessaire (au moins dans Cygwin).


CA se peut. Si votre code va toujours être utilisé dans la manière dont vous vous attendez, il est logique de faire de judicieux compromis ...


@Stuartreynolds: L'utilisation d'un fichier temporaire ne le rendra pas plus lentement, sauf peut-être pour un moment constant négligeable pour renommer le fichier à son nom d'origine.


C'est un bon point ... Si vous faites une copie au lieu de renommé, vous pouvez prendre une performance inutile.


@Juliano. J'ai trouvé Renommer des fichiers pour être très lent dans Windows (Cygwin). (Plusieurs fois plus lentement que Linux sur la même machine). En règle générale, j'essaie de l'éviter si vous travaillez sur de très nombreux fichiers et si l'utilisateur attend pendant que le script s'exécute.



6
votes

Vous recherchez éponge .


4 commentaires

Cela ressemble à une bonne solution, sauf que je ne souhaite pas nécessiter tous les utilisateurs de mes scripts pour installer des dépendances supplémentaires (ou compiler n'importe quel code). -L'êtes pas une alternative utilisant des utilitaires standard ou des fonctionnalités de coquille intégrées?


Je ne recommande pas l'éponge. Si une commande dans votre pipeline (autre que l'éponge) échoue (par exemple, en raison de l'erreur de syntaxe, des arguments non valides, etc.), il efface le fichier et vous terminez sans l'original et le fichier de destination.


/ TMP peut être monté en mémoire (au moins sous Linux). Dans ce cas, j'espère que cela pourrait être vraiment rapide. Pas sûr de / TMP en cygwin cependant. Cygwin tire-t-il cela en mémoire?


@Julinao - Le problème n'est pas une éponge, son shell i) Sponge Fichier, Cause Fichier à tronquer. De même Cat File | b | c | Sponge> Fichier, est également tronqué. Bash tronque le fichier avant que l'éponge ne puisse voir l'entrée. ii) fichier de chat | Fichier d'éponge, fonctionne bien.



5
votes

L'utilisation d'un fichier temporaire est la solution correcte ici. Lorsque vous utilisez une redirection comme '>', il est géré par la coquille, et peu importe le nombre de commandes dans votre pipeline, la coquille est libre de supprimer et d'écraser le fichier de sortie avant que toute commande soit exécutée (pendant la configuration du pipeline).


0 commentaires

2
votes

Utilisation mktemp (1) ou TEMPFILE (1) Enregistre la dépense de devoir penser que le nom de fichier unique.


0 commentaires

1
votes

Je pense que le meilleur moyen est d'utiliser un fichier temp. Toutefois, si vous souhaitez une autre approche, vous pouvez utiliser quelque chose comme awk pour tamponner l'entrée dans la mémoire avant que votre application ne commence à recevoir une entrée. Le script suivant tamponnera la totalité de l'entrée dans le tableau LIGNES Avant de commencer à la sortie au prochain consommateur de la pipeline. XXX

Vous pouvez l'effondrer dans une doublure si vous voulez: xxx

avec tout cela, je vous recommande toujours d'utiliser un fichier temporaire pour la sortie, puis écrasez le fichier d'origine avec celui-ci. < / p>


0 commentaires

6
votes

Comme beaucoup d'autres, j'aime utiliser des fichiers temporaires. J'utilise l'identifiant de processus shell-identifiant dans le nom temporaire de sorte que si plusieurs copies du script fonctionnent simultanément, elles ne seront pas en conflit. Enfin, je ne remplace ensuite que le fichier d'origine si le script réussit (en utilisant le court-circuit de l'opérateur booléen - c'est un peu dense mais très agréable pour des lignes de commande simples). Mettre tout ensemble, il ressemblerait à: xxx

Cela laissera le fichier temporaire si la commande échoue. Si vous souhaitez nettoyer sur une erreur, vous pouvez modifier cela à: xxx

BTW, je me suis débarrassé de la mauvaise utilisation du chat et l'a remplacé par une redirection d'entrée.


4 commentaires

Merci - c'est un bon tour. Vous allez étancher un fichier si quelqu'un échoue quelque_script. Besoin de gérer ce cas: "( smscrpt. $$ && mv smscrpt. Dossier $$) || \ rm -f smscrpt. $$" Pour toujours, préférerait quelque chose comme: "(Systint Fichier "parce que (i) son chemin plus facile à lire, (ii) Je n'ai pas à retenir de mettre en vérification des erreurs (iii), je crois que cela fonctionnerait assez plus vite sous Cygwin en raison de l'accès du fichier lent déviant.


@StuartreyNolds - Quelqu'un d'autre a posté à propos de l'éponge et vous avez rejeté cela parce que ce n'est pas standard. Il n'y a rien de standard qui fait ce que vous voulez préférer.


@klatchko - Je pense que quelque chose comme éponge est la réponse que je recherche (avec les mises en garde que j'ai mentionnées - ce n'est pas vraiment facile pour moi de l'utiliser largement). Imo, s'il n'y a vraiment rien qui fait ce que fait l'éponge, et la fonctionnalité de l'éponge est fondamentale pour les scripts shell (mise en mémoire tampon pour éviter que la corruption ne me semble assez fondamentale), alors il devrait probablement faire partie de bash , ou la norme GNU Toolset (dans quel cas j'espérais que quelqu'un stipera pourquoi nous n'avons pas besoin d'éponge du tout ... Toute personne?). Est-ce que je vraiment doit faire un fichier temporaire pour faire cela?


@stuartreynolds - Si vous voulez quelque chose de standard à compter d'aujourd'hui, vous avez besoin de fichiers temporaires. Je suis en désaccord que la mise en mémoire tampon est fondamentale car vous obtenez votre comportement nécessaire avec des fichiers temporaires (et donné comment les lignes de commande fonctionnent, des fichiers temporaires sont meilleurs, car vous pouvez conserver votre fichier d'origine s'il y a une erreur). Enfin, si Cygwin est tellement brisé, ce fichier Renommer est trop lent, c'est la question qui devrait être corrigée.



1
votes

en réponse à La question de l'opération ci-dessus a> sur l'utilisation de éponge code> sans dépendances externes et construction sur @ d.shawley's Réponse , vous Peut avoir l'effet de l'éponge avec seulement une dépendance sur GAWK code>, qui n'est pas rare sur des systèmes UNIX ou UNIX-SIMS:

#!/bin/bash
cat "$1" | gawk -voutfn="$1" '{lines[NR]=$0;} END {if(NR>0){print lines[1]>outfn;} for(i=2;i<=NR;++i) print lines[i] >> outfn;}'


0 commentaires

1
votes

Je pense que vous devez utiliser mktemp . Quelque chose comme ça fonctionnera: xxx


0 commentaires

2
votes

Une autre option est simplement de lire le fichier dans une variable: xxx


0 commentaires