En raison d'un bug d'application non encore diagnostiqué, j'ai plusieurs centaines de serveurs avec un disque plein. Il y a un fichier qui a été rempli de lignes en double - pas un fichier journal, mais un fichier d'environnement utilisateur avec des définitions de variables (donc je ne peux pas simplement supprimer le fichier).
J'ai écrit une sed
commande simple pour vérifier les lignes ajoutées par erreur et les supprimer, et je l'ai testée sur une copie locale du fichier. Cela a fonctionné comme prévu.
Cependant, lorsque je l'ai essayé sur le serveur avec le disque plein, j'ai eu environ l'erreur suivante (c'est de la mémoire, pas de copier-coller):
sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname
Bien sûr, je sais qu'il n'y a plus d'espace. C'est pourquoi j'essaie de supprimer des trucs! (La sed
commande que j'utilise réduira un fichier de plus de 4000 lignes à environ 90 lignes.)
Ma sed
commande est justesed -i '/myregex/d' /path/to/file/filename
Existe-t-il un moyen d'appliquer cette commande malgré le disque plein?
(Il doit être automatisé, car je dois l'appliquer à plusieurs centaines de serveurs comme solution rapide.)
(Évidemment, le bogue d'application doit être diagnostiqué, mais en attendant, les serveurs ne fonctionnent pas correctement ....)
Mise à jour: La situation à laquelle j'ai été confrontée a été résolue en supprimant quelque chose d'autre que j'ai découvert que je pouvais supprimer, mais j'aimerais toujours la réponse à cette question, qui serait utile à l'avenir et pour d'autres personnes.
/tmp
est un non-go; c'est sur le même système de fichiers.
Avant de libérer de l'espace disque, j'ai testé et découvert que je pouvais supprimer les lignes en vi
ouvrant le fichier et en l'exécutant :g/myregex/d
, puis en enregistrant avec succès les modifications avec :wq
. Il semble qu'il devrait être possible d'automatiser cela, sans avoir recours à un système de fichiers distinct pour contenir un fichier temporaire .... (?)
la source
sed -i
crée une copie temporaire pour fonctionner. Je soupçonne que ceed
serait mieux pour cela, bien que je ne sois pas assez familier pour proscrire une solution réelleed
vous couriez:printf %s\\n g/myregex/d w q | ed -s infile
mais gardez à l'esprit que certaines implémentations utilisent également des fichiers temporaires commesed
(vous pouvez essayer busybox ed - afaik ne crée pas de fichier temporaire)echo
. utiliserprintf
. et faitessed
ajouter quelques caractères que vous déposez à la dernière ligne pour éviter de perdre des blancs de fin. De plus, votre shell doit être capable de gérer l'intégralité du fichier sur une seule ligne de commande. c'est votre risque - testez d'abord.bash
est particulièrement mauvais à cela (je pense que c'est à faire avec un espace de pile?) et peut vous fatiguer à tout moment. les deuxsed
'si recommandés utiliseraient au moins le tampon de pipe du noyau pour faire bon effet entre eux, mais la méthode est assez similaire. votre sous-commande commandera égalementfile
si le sed w / in réussit ou non.sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}
et si cela fonctionne, lisez le reste de ma réponse.Réponses:
L'
-i
option n'écrase pas vraiment le fichier d'origine. Il crée un nouveau fichier avec la sortie, puis le renomme avec le nom de fichier d'origine. Comme vous n'avez pas de place sur le système de fichiers pour ce nouveau fichier, il échoue.Vous devrez le faire vous-même dans votre script, mais créez le nouveau fichier sur un système de fichiers différent.
De plus, si vous supprimez simplement des lignes qui correspondent à une expression rationnelle, vous pouvez utiliser à la
grep
place desed
.En général, il est rarement possible pour les programmes d'utiliser le même fichier en entrée et en sortie - dès qu'il commence à écrire dans le fichier, la partie du programme qui lit le fichier ne verra plus le contenu d'origine. Il doit donc d'abord copier le fichier d'origine quelque part, ou écrire dans un nouveau fichier et le renommer une fois terminé.
Si vous ne souhaitez pas utiliser de fichier temporaire, vous pouvez essayer de mettre en cache le contenu du fichier en mémoire:
la source
rsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"
d' icised -i
cela préserve ce genre de choses?sed -i
ne conserve aucune de ces choses. Je viens de l'essayer avec un fichier que je ne possède pas, mais situé dans un répertoire que je possède, et cela m'a permis de remplacer le fichier. Le remplacement appartient à moi, pas au propriétaire d'origine.var=$(< FILE); echo "$FILE" | grep '^"' > FILE
v=$(<file)&& printf %s\\n "$v" >file
mais vous ne l' utilisez pas même&&
. Le demandeur parle de l'exécuter dans un script - automatiser l'écrasement d'un fichier avec une partie de lui-même. vous devez au moins valider que vous pouvez ouvrir avec succès l'entrée et la sortie. De plus, le shell pourrait exploser.Voilà comment ça
sed
marche. Si utilisé avec-i
(modifier sur place)sed
crée un fichier temporaire avec le nouveau contenu du fichier traité. Une fois terminésed
, remplace le fichier de travail actuel par le fichier temporaire. L'utilitaire ne modifie pas le fichier sur place . C'est exactement le comportement de chaque éditeur.C'est comme si vous exécutiez la tâche suivante dans un shell:
À ce stade
sed
, essaie de vider les données mises en mémoire tampon dans le fichier mentionné dans le message d'erreur avec l'fflush()
appel système:Pour votre problème, je vois une solution dans le montage d'un système de fichiers séparé (par exemple
tmpfs
, si vous avez suffisamment de mémoire ou un périphérique de stockage externe) et déplacer certains fichiers là-bas, les traiter là-bas et les reculer.la source
Depuis la publication de cette question, j'ai appris qu'il
ex
s'agit d'un programme compatible POSIX. Il est presque universellement lié àvim
, mais de toute façon, ce qui suit est (je pense) un point cléex
à propos des systèmes de fichiers (extrait de la spécification POSIX):"... affectera n'importe quel fichier ..." Je crois que mettre quelque chose sur le système de fichiers (même un fichier temporaire) compterait comme "affectant n'importe quel fichier". Peut être?*
Une étude attentive des spécifications POSIX pour
ex
indiquer certains "pièges" sur son utilisation portable prévue par rapport aux utilisations scriptées courantes de laex
recherche en ligne (qui sont jonchées devim
commandes spécifiques).+cmd
est facultative selon POSIX.-c
options est également facultatif.:g
"mange" tout jusqu'au prochain saut de ligne non échappé (et l'exécute donc après chaque correspondance trouvée pour l'expression régulière plutôt qu'une fois à la fin). Alors-c 'g/regex/d | x'
supprime seulement une instance, puis quitte le fichier.Donc, selon ce que j'ai recherché, la méthode compatible POSIX pour éditer sur place un fichier sur un système de fichiers complet pour supprimer toutes les lignes correspondant à une expression rationnelle spécifique, est:
Cela devrait fonctionner si vous disposez de suffisamment de mémoire pour charger le fichier dans un tampon.
* Si vous trouvez quelque chose qui indique le contraire, veuillez le mentionner dans les commentaires.
la source
ex +g/match/d -scx file
est également compatible POSIX?vi
fonctionné sur un système de fichiers complet, je pense que dans la plupart des cas, cela fonctionneraitex
également - mais peut-être pas pour un fichier ginormous.sed -i
ne fonctionne pas sur un système de fichiers complet quelle que soit sa taille.Utilisez le tuyau, Luke!
Lire le fichier | filtre | répondre
dans ce cas,
sed
ne crée pas de nouveau fichier et envoie simplement une sortie redirigée versdd
laquelle ouvre le même fichier . Bien sûr, on peut utilisergrep
dans un cas particulierpuis tronquez le reste.
la source
sed
toujours des fichiers temporaires?grep
de toute façon passponge
commande. Oui,sed
avec-i
toujours crée des fichiers lilke "seduyUdmw" avec 000 droits.Comme indiqué dans d'autres réponses,
sed -i
fonctionne en copiant le fichier dans un nouveau fichier dans le même répertoire , en apportant des modifications au processus, puis en déplaçant le nouveau fichier sur l'original. Voilà pourquoi cela ne fonctionne pas.ed
(l'éditeur de ligne d'origine) fonctionne d'une manière quelque peu similaire, mais, la dernière fois que j'ai vérifié, il utilise/tmp
le fichier de travail. Si votre/tmp
système de fichiers est différent de celui qui est plein, iled
peut faire le travail pour vous.Essayez ceci (à l'invite de votre shell interactif):
Le
P
(qui est un P majuscule ) n'est pas strictement nécessaire. Il active les invites; sans cela, vous travaillez dans le noir, et certaines personnes trouvent cela déconcertant. Lesw
etq
sont w rite et q uit.Si votre
/tmp
répertoire se trouve sur le système de fichiers qui est plein (ou si son système de fichiers est également plein), essayez de trouver de l'espace quelque part. le chaos a mentionné le montage d'un tmpfs ou d'un périphérique de stockage externe (par exemple, un lecteur flash); mais, si vous avez plusieurs systèmes de fichiers et qu'ils ne sont pas tous pleins, vous pouvez simplement utiliser l'un des autres systèmes existants. chaos suggère de copier le (s) fichier (s) vers l'autre système de fichiers, de les éditer là (avecsed
), puis de les recopier. À ce stade, c'est peut-être la solution la plus simple. Mais une alternative serait de créer un répertoire accessible en écriture sur un système de fichiers qui a de l'espace libre, de définir une variable d'environnementTMPDIR
pour pointer vers ce répertoire, puis de l'exécutered
. (Divulgation: je ne sais pas si cela fonctionnera, mais cela ne peut pas faire de mal.)Une fois que vous commencez à
ed
travailler, vous pouvez automatiser cela en faisantdans un script. Ou , comme l'a suggéré don_crissti.
printf '%s\n' 'g/myregex/d' w q | ed -s filename
la source
ed
ou avecex
) de telle sorte que la mémoire soit utilisée plutôt qu'un système de fichiers séparé? C'est ce que j'allais vraiment faire (et la raison pour laquelle je n'ai pas accepté de réponse.)ed
nombreuses années. Il y avait encore des choses comme les ordinateurs 16 bits, sur lesquels les processus étaient limités à un espace d'adressage de 64 Ko (!), Donc l'idée d'un éditeur lisant le fichier entier en mémoire n'était pas un démarreur. Depuis lors, bien sûr, la mémoire est devenue plus grande - mais les disques et les fichiers aussi. Étant donné que les disques sont si gros, les gens ne ressentent pas le besoin de faire face à la contingence de/tmp
manquer d'espace. Je viens de jeter un coup d'œil au code source d'une version récente deed
, et il semble toujours… (suite)ed
(ouex
ouvi
) offre une option pour garder le tampon en mémoire. D'un autre côté, l' édition de texte avec ed et vi - Chapitre 11: Traitement de texte - Partie II: Exploration de Red Hat Linux - Secrets professionnels de Red Hat Linux 9 - Les systèmes Linux disent queed
le tampon d'édition réside dans la mémoire,… (suite) )vi
(qui est le même programme queex
). Je crois qu'ils utilisent simplement une formulation bâclée et imprécise - mais, si c'est sur Internet (ou sur papier), cela doit être vrai, non? Vous payez votre argent et vous faites votre choix.Vous pouvez tronquer le fichier assez facilement si vous pouvez obtenir le nombre d'octets à votre décalage et vos lignes se produisent d'un point de départ jusqu'à la fin.
Ou bien si votre
${TMPDIR:-/tmp}
est sur un autre système de fichiers peut-être:Parce que (la plupart) des coquilles y placent leurs documents ici dans un fichier temporaire supprimé. Il est parfaitement sûr tant que le
<<FILE
descripteur est maintenu du début à la fin et${TMPDIR:-/tmp}
dispose d'autant d'espace que nécessaire.Les shells qui n'utilisent pas de fichiers temporaires utilisent des tuyaux, et ne sont donc pas sûrs à utiliser de cette façon. Ces coquilles sont généralement
ash
dérivés commebusybox
,dash
, BSDsh
-zsh
,bash
,ksh
et le shell Bourne, cependant, tous les fichiers temporaires d'utilisation.apparemment, j'ai écrit un petit programme shell en juillet dernier pour faire quelque chose de très similaire
Si ce
/tmp
n'est pas viable, tant que vous pouvez mettre le fichier en mémoire quelque chose comme ...... comme un cas général garantirait au moins que le fichier a été entièrement tamponné par le premier
sed
processus avant d'essayer de tronquer le fichier d'entrée / sortie.Une solution plus ciblée - et efficace - pourrait être:
... car cela ne dérangerait pas les lignes de mise en mémoire tampon que vous vouliez supprimer de toute façon.
Un test du cas général:
la source
/tmp
qui se trouvent sur le même système de fichiers. J'aime ta doublesed
version. Je pense qu'une combinaison de Barmar et de votre réponse serait probablement la meilleure, quelque chose comme:myvar="$(sed '/myregex/d' < file)" && [ -n "$myvar" ] && echo "$myvar" > file ; unset myvar
(Dans ce cas, je ne me soucie pas de préserver les nouvelles lignes de fin.)sed
|cat
La chose ci-dessus n'ouvre jamais la sortie à moins qu'ellesed
n'ait déjà mis en mémoire tampon le fichier entier et qu'elle soit prête à commencer à l'écrire entièrement en sortie. S'il essaie de mettre le fichier en mémoire tampon et échoue - échoueread
car il trouve EOF sur le|
canal avant de lire sa première nouvelle ligne etcat >out
ne se produit donc jamais jusqu'à l'heure de l'écrire entièrement de la mémoire. un débordement ou quelque chose comme ça échoue. également le pipeline entier retourne le succès ou l'échec à chaque fois. le stocker dans une var est juste plus risqué.file=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shite
donc le fichier de sortie et le var seraient écrits simultanément, ce qui ferait soit une sauvegarde efficace , soit la seule raison pour laquelle vous voudriez compliquer les choses plus que ce dont vous auriez besoin.read script
etread v
dans votre réponse. Si vous pouvez en dire plus, je serai très apprécié, merci!$script
est lesed
script que vous utiliseriez pour cibler la partie de votre fichier que vous souhaitez; c'est le script qui vous donne le résultat final que vous souhaitez dans le flux.v
est juste un espace réservé pour une ligne vide. dans unbash
shell, ce n'est pas nécessaire carbash
il utilisera automatiquement la$REPLY
variable shell à sa place si vous n'en spécifiez pas, mais POSIXly vous devriez toujours le faire. je suis content que vous le trouviez utile, au fait. Bonne chance. im mikeserv @ gmail si vous avez besoin de quelque chose en profondeur. je devrais avoir un ordinateur à nouveau dans quelques joursCette réponse emprunte des idées à cette autre réponse et à cette autre réponse, mais s'appuie sur elles, créant une réponse plus généralement applicable:
La première ligne exécute la
sed
commande avec une sortie écrite sur la sortie standard (et non dans un fichier); spécifiquement, à un tuyauwc
pour compter les caractères. La deuxième ligne exécute également lased
commande avec une sortie écrite sur la sortie standard, qui, dans ce cas, est redirigée vers le fichier d'entrée en mode de lecture / écriture par écrasement (pas de troncature), qui est abordé ici . C'est une chose quelque peu dangereuse à faire; il n'est sûr que lorsque la commande de filtre n'augmente jamais la quantité de données (texte); c'est-à-dire que pour chaque n octet qu'il lit, il écrit n octets ou moins. C'est, bien sûr, vrai pour lased '/myregex/d'
commande; pour chaque ligne qu'il lit, il écrit exactement la même ligne, ou rien. (Autres exemples:s/foo/fu/
ous/foo/bar/
serait en sécurité, maiss/fu/foo/
ets/foo/foobar/
ne serait pas.)Par exemple:
parce que ces 32 octets de données:
a été remplacé par ces 25 caractères:
laissant les sept octets
night.\n
à la fin.Enfin, la
dd
commande recherche la fin des nouvelles données nettoyées (octet 25 dans cet exemple) et supprime le reste du fichier; c'est-à-dire qu'il tronque le fichier à ce point.Si, pour une raison quelconque, l'
1<>
astuce ne fonctionne pas, vous pouvez le faireNotez également que tant que vous ne faites que supprimer des lignes, tout ce dont vous avez besoin est
grep -v myregex
(comme l'a souligné Barmar ).la source
sed -i 'd' / chemin / vers / fichier / nom de fichier
la source