Comment rechercher un fichier texte pour un modèle et de le remplacer par une valeur donnée
Je suis à la recherche d'un script pour rechercher un fichier (ou une liste de fichiers) pour un modèle et, si trouvé, remplacer ce modèle avec une valeur donnée.
Pensées?
- Les réponses ci-dessous, sachez que toutes les recommandations pour l'utilisation
File.read
besoin d'être atténuée avec l'information dans stackoverflow.com/a/25189286/128421 pourquoi aspirer de gros fichiers, c'est mauvais. Aussi, au lieu deFile.open(filename, "w") { |file| file << content }
variations utilisationFile.write(filename, content)
.
Vous devez vous connecter pour publier un commentaire.
Avertissement: Cette approche est un naïf illustration de ruby capacités, et non pas une production de qualité de la solution pour le remplacement de chaînes de caractères dans les fichiers. Il est sujet à divers scénarios d'échec, tel que perte de données en cas de crash, interrompre, ou le disque est plein. Ce code n'est pas adapté pour rien au-delà d'un moyen rapide d'un script où toutes les données sont sauvegardées. Pour cette raison, ne pas copier ce code dans vos programmes.
Voici un petit court chemin pour le faire.
File.write(file_name, text.gsub(/regexp/, "replace")
"w"
veux dire?"w"
signifie "écriture seule". C'est un ouvrir en mode qui indique le système d'exploitation de ce type de descripteur de fichier que vous souhaitez en fonction de votre intention. Dans"w"
mode spécifiquement vous racontez l'OS que vous voulez écrire dans ce fichier, et il va commencer à écrire dès le début, la troncation de tout contenu existant. Sous le capot, ces modes de devenir des indicateurs transmis dans le ouvrir le système d'appel.En fait, Ruby possède une fonction de modification. Comme Perl, vous pouvez dire
Cela permettra d'appliquer le code dans les guillemets pour tous les fichiers dans le répertoire courant dont le nom se termine par ".txt". Des copies de sauvegarde des fichiers modifiés sera créé avec une ".bak" extension ("foobar.txt.bak" je pense).
REMARQUE: cela ne semble pas fonctionner pour plusieurs lignes de recherche. Pour ceux, que vous avez à faire il les autres moins jolie façon, avec un script de lancement autour de la regex.
<main>': undefined method
gsub " pour les principaux:Objet (NoMethodError).bak
est l'extension utilisée pour un fichier de sauvegarde (en option).-p
est quelque chose commewhile gets; <script>; puts $_; end
. ($_
est la dernière ligne de lecture, mais vous pouvez l'attribuer à quelque chose commeecho aa | ruby -p -e '$_.upcase!'
.)jruby -pi.bak -e "$_.gsub!(/oldtext/){|x| x.upcase}" *.txt
jruby -pi.bak -e "gsub(/oldtext/){|x| x.upcase}" *.txt
Gardez à l'esprit que, lorsque vous faites cela, le système de fichiers est peut-être hors de l'espace et vous pouvez créer un fichier de longueur nulle. C'est catastrophique, si vous êtes en train de faire quelque chose comme de l'écriture /etc/passwd fichiers tant que partie du système de gestion de la configuration.
[ EDIT: a noter que l'édition de fichier comme dans l'acceptation de réponse sera toujours tronquer le fichier et copiez le nouveau fichier de manière séquentielle. Il y aura toujours une condition de course où le cumul des lecteurs verront un fichier tronqué. Si le processus est interrompu pour une raison quelconque (ctrl-c, OOM killer, plantage du système, panne de courant, etc) au cours de l'écriture, puis le fichier tronqué également être à gauche, qui peut être catastrophique. C'est le genre de dataloss scénario où les développeurs DOIVENT considérer, car il va se passer. Pour cette raison, je pense que l'on a accepté la réponse devrait probablement pas la accepté de répondre. Au minimum d'écrire dans un fichier temporaire et déplacer/renommer le fichier en place comme la "simplicité" de la solution à la fin de cette réponse.]
Vous avez besoin d'utiliser un algorithme qui:
lit l'ancien fichier et écrit dans le nouveau fichier. (Vous devez être prudent au sujet de qui sucent ensemble de fichiers en mémoire).
explicitement ferme le nouveau fichier temporaire, qui est l'endroit où vous pouvez lancer une exception parce que les tampons de fichier ne peuvent pas être écrites sur le disque, car il n'y a pas d'espace. (L'intercepter et de le nettoyage le fichier temporaire si vous le souhaitez, mais vous avez besoin de renvoyer quelque chose ou ne parviennent pas assez dur à ce point.
fixe les autorisations de fichier et de modes sur le nouveau fichier.
renomme le fichier et le place en place.
Avec les systèmes de fichiers ext3 vous avez la garantie que les métadonnées écrire pour déplacer le fichier en place ne sont réarrangées par le système de fichiers et les écrits avant de les tampons de données pour le nouveau fichier sont écrites, ce qui devrait réussissent ou échouent. Le système de fichiers ext4 a également été patché pour soutenir ce genre de comportement. Si vous êtes vraiment paranoïaque, vous devriez appeler le
fdatasync()
appel système comme une étape 3.5 avant de déplacer le fichier en place.Indépendamment de la langue, c'est la meilleure pratique. Dans les langues où l'appelant
close()
ne pas lever une exception (Perl ou C) vous devez explicitement vérifier le retour declose()
et de lever une exception en cas d'échec.La suggestion ci-dessus pour simplement slurp le fichier dans la mémoire, de la manipuler et de l'écrire dans le fichier seront garantis pour produire des fichiers de longueur nulle sur un système de fichiers complet. Vous devez toujours utilisation
FileUtils.mv
pour déplacer un écrit de fichier temporaire en place.Une dernière considération est l'emplacement du fichier temporaire. Si vous ouvrez un fichier dans /tmp ensuite, vous devez tenir compte de quelques problèmes:
Probablement plus important encore, lorsque vous essayez de
mv
le fichier sur un appareil mont vous de manière transparente converties encp
comportement. L'ancien fichier sera ouvert, les anciens fichiers de l'inode sera préservé et rouvert et le contenu du fichier sera copié. Ce n'est probablement pas ce que vous voulez, et vous pouvez exécuter en "fichier texte occupé" erreurs si vous essayez de modifier le contenu d'un fichier. Ceci est également défait le but de l'utilisation du système de fichiersmv
commandes et vous pouvez exécuter le système de fichiers de destination de l'espace avec seulement partiellement écrite fichier.Également, cela n'a rien à voir avec le Rubis de la mise en œuvre. Le système
mv
etcp
commandes comportent de la même façon.Ce qui est plus préférable d'ouvrir un fichier temporaire dans le même répertoire que l'ancien fichier. Cela garantit qu'il n'y aura pas de cross-device déplacer les problèmes. Le
mv
lui-même ne doit jamais échouer, et vous devriez toujours obtenir un complet et untruncated fichier. Toute défaillance, tels que l'appareil de l'espace, des erreurs d'autorisation, etc., devrait être rencontrés lors de l'écriture du fichier temporaire sur.Les seuls inconvénients de l'approche de la création du fichier temporaire dans le répertoire de destination sont:
Voici un code qui implémente le plein algorithme (windows code n'est pas testé et inachevé):
Et ici est un peu plus version ne vous inquiétez pas à propos de tous les possibles cas de bord (si vous êtes sous Unix et ne se soucient pas de l'écriture dans /proc):
Vraiment le cas simple d'utilisation, lorsque vous n'avez pas de soins sur les autorisations de système de fichiers (vous n'êtes pas en cours d'exécution en tant que root, ou vous êtes en cours d'exécution en tant que root et le fichier est à la racine de la propriété):
TL;DR: Qui doit être utilisé au lieu de la accepté de répondre à un minimum, dans tous les cas, afin d'assurer la mise à jour est atomique et de lecteurs simultanés ne verrez pas les fichiers tronqués. Comme je l'ai mentionné ci-dessus, la création du fichier temporaire dans le même répertoire que le fichier modifié est important afin d'éviter la croix appareil mv opérations traduites en cp opérations si /tmp est monté sur un appareil différent. L'appel de fdatasync est une couche supplémentaire de la paranoïa, mais il va subir une dégradation des performances, j'ai donc omis de cet exemple, car il n'est pas couramment pratiqué.
Il n'est pas vraiment un moyen de modifier les fichiers en place. Ce que vous faites habituellement lorsque vous pouvez sortir avec elle (c'est à dire si les fichiers ne sont pas trop gros), vous lire le fichier dans la mémoire (
File.read
), effectuer vos remplacements sur la chaîne (String#gsub
) et puis les écrire la modification de la chaîne vers le fichier (File.open
,File#write
).Si les fichiers sont assez gros pour que ce soit impossible, ce que vous devez faire, est de lire le fichier en morceaux (si le modèle que vous souhaitez remplacer, de ne pas s'étendre sur plusieurs lignes puis un morceau signifie généralement une seule ligne - vous pouvez utiliser
File.foreach
pour lire un fichier ligne par ligne), et pour chaque bloc d'effectuer la substitution sur elle et l'ajouter à un fichier temporaire. Lorsque vous avez terminé de parcourir le fichier source, vous la fermez et l'utilisationFileUtils.mv
pour le remplacer par le fichier temporaire.Une autre approche est l'utilisation directe de l'édition à l'intérieur de Ruby (et non à partir de la ligne de commande):
Si vous ne voulez pas créer une sauvegarde, puis le changement".bak " pour ".
read
) le fichier. Il est évolutif et doit être très rapide.Voici une solution pour rechercher/remplacer dans tous les fichiers d'un répertoire donné. Fondamentalement, j'ai pris la réponse fournie par sepp2k et l'agrandit.
Cela fonctionne pour moi:
Si vous avez besoin de faire des substitutions à travers la ligne des frontières, alors à l'aide de
ruby -pi -e
ne fonctionnera pas parce que lesp
processus d'une ligne à la fois. Au lieu de cela, je recommande ce qui suit, même si elle peut échouer avec un multi-GO fichier:L'est à la recherche d'espace blanc (y compris éventuellement les nouvelles lignes) à la suite d'une citation, auquel cas il se débarrasse de l'espace. Le
%q(')
est juste une façon élégante de citer le caractère de devis.Voici une alternative à la un paquebot de jim, cette fois dans un script
L'enregistrer dans un script, par exemple, remplacer.rb
Vous commencez sur la ligne de commande avec
*.txt peut être remplacée par une autre sélection ou avec certains noms de fichiers ou chemins
cassé vers le bas de sorte que je ne peux expliquer ce qui se passe, mais encore exécutable