J'essayais de modifier rapidement un .hgignore
fichier à partir du shell bash Cygwin aujourd'hui, et j'ai ajouté une ligne qui était une erreur. Je ne sais pas si c'était la meilleure façon de le faire, mais j'ai rapidement pensé head -1 .hgignore
à supprimer la ligne incriminée (je n'avais auparavant qu'une seule ligne dans le fichier). Effectivement, une fois exécuté, il donne la première ligne comme seule sortie.
Mais lorsque j'ai essayé de rediriger la sortie et de réécrire le fichier à l'aide head -1 .hgignore > .hgignore
, le fichier était vide. Pourquoi cela arrive-t-il? Si j'essaie d'ajouter à la place, head -1 .hgignore >> .hgignore
cela s'ajoute correctement, mais ce n'est évidemment pas le résultat souhaité. Pourquoi une redirection tronquée ne fonctionne-t-elle pas dans ce cas?
la source
cut
modifier un fichier en place? , Comment puis-je faire iconv remplacer le fichier d'entrée par la sortie convertie?Réponses:
Lorsque le shell obtient une ligne de commande comme:
command > file.out
le shell lui-même ouvre (et crée peut-être) le fichier nomméfile.out
. Le shell définit le descripteur de fichier 0 sur le descripteur de fichier obtenu à partir de l'ouverture. Voilà comment fonctionne la redirection d'E / S: chaque processus connaît les descripteurs de fichiers 0, 1 et 2.La partie difficile à ce sujet est de savoir comment ouvrir
file.out
. La plupart du temps, vous voulezfile.out
ouvrir pour l'écriture à l'offset 0 (c'est-à-dire tronqué) et c'est ce que le shell a fait pour vous. Il a tronqué .hgignore, l'a ouvert pour l'écriture, a dupé le descripteur de fichier à 0, puis l'a exécutéhead
. Clobber fichier instantané.Dans bash shell, vous faites un
set noclobber
pour changer ce comportement.la source
Je pense que Bruce répond à ce qui se passe ici avec le pipeline shell.
Un de mes petits utilitaires préférés est la
sponge
commande de moreutils . Il résout exactement ce problème en "absorbant" toutes les entrées disponibles avant d'ouvrir le fichier de sortie cible et d'écrire les données. Il vous permet d'écrire des pipelines exactement comme vous vous attendiez à:La solution du pauvre est de diriger la sortie vers un fichier temporaire, puis une fois la ligne de repère terminée (par exemple la prochaine commande que vous exécutez) consiste à déplacer le fichier temporaire vers l'emplacement du fichier d'origine.
la source
head -1 .hgignore | tee .hgignore
?tee
est dans coreutils, et comme un avantage / effet secondaire, cela écrit également à STDOUTtee
ouvre et tronque le fichier dans lequel il écrit lorsqu'il est instancié comme tout le reste, de sorte qu'il ne résout pas le problème principal ici de la condition de concurrence sur la lecture du contenu du fichier avant de le tronquer avec l'écriture.tee
semble faire la chose souhaitée. J'ai une version8.13
sur ma machine.tee
programme tronquera vos fichiers, il n'est pas configuré pour les mettre en double tampon.Dans
file
est tronqué avant lehead
démarrage, mais si vous l'écrivez:ce n'est pas ce qui
file
est ouvert en mode lecture-écriture. Cependant, une fois l'head
écriture terminée, il ne tronque pas le fichier, donc la ligne ci-dessus serait un no-op (head
juste réécrire la première ligne sur elle-même et laisser les autres intactes).Cependant, après
head
son retour et pendant que lefd
est toujours ouvert, vous pouvez appeler une autre commande qui effectue letruncate
.Par exemple:
Ce qui importe ici, c'est que
truncate
ci - dessus,head
déplace simplement le curseur pour fd 1 à l'intérieur du fichier juste après la première ligne. Il réécrit la première ligne dont nous n'avions pas besoin, mais ce n'est pas dangereux.Avec une tête POSIX, nous pourrions réellement nous en tirer sans réécrire cette première ligne:
Ici, nous utilisons le fait qui
head
déplace la position du curseur dans son stdin. Bienhead
que lise généralement son entrée par gros morceaux pour améliorer les performances, POSIX l'oblige (dans la mesure du possible) àseek
reculer juste après la première ligne s'il l'a dépassée. Notez cependant que toutes les implémentations ne le font pas.Alternativement, vous pouvez utiliser la
read
commande du shell à la place dans ce cas:la source
STDIN
similaire à ce que vous avez accompli en utilisantperl
cidd
peut cependant tronquer à n'importe quel décalage absolu arbitraire dans le fichier. Vous pouvez donc déterminer le décalage en octets de la deuxième ligne et tronquer à partir de là avecdd bs=1 seek="$offset" of=file
La solution du vrai homme est
ou en une ligne
Ou avec GNU sed:
(Non, je plaisante. J'utiliserais un éditeur interactif.
vi .hgignore
GddZZ
)la source
:wq
OverZZ
?:x
ce que mes doigts font automatiquementZQ
est le même que:q!
Vous pouvez utiliser Vim en mode Ex:
2,
sélectionnez les lignes 2 jusqu'à la find
supprimerx
sauver et fermerla source
Pour l'édition de fichiers sur place, vous pouvez également utiliser l'astuce de gestion des fichiers ouverts comme indiqué par Jürgen Hötzel dans Rediriger la sortie de sed / s / c / d / 'myFile vers myFile .
la source
rm .hgignore
une panne de courant, vous évitez des heures de dur labeur. D'accord, cela n'a pas d'importance.hgignore
, mais pourquoi feriez-vous quelque chose de compliqué de toute façon? D'où mon downvote: techniquement correct mais une très mauvaise idée.perl -i
(pour l'édition sur place), et je ne serais pas surpris si certaines implémentations de lesed -i
faisaient aussi (bien que la dernière version de GNUsed
ne semble pas le faire).