Puis-je faire `couper 'changer un fichier en place?

17

La manpage ne me donne pas beaucoup d'espoir, mais j'espère que c'est une fonctionnalité non documentée (et / ou spécifique à GNU).

Hank Gay
la source

Réponses:

14

Tu ne peux pas. Utilisez ed ou GNU sed ou perl, ou faites ce qu'ils font en arrière-plan, c'est-à-dire créer un nouveau fichier pour le contenu.

edportable:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

cut, création d'un nouveau fichier (recommandé, car si votre script est interrompu, vous pouvez simplement le relancer):

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cut, en remplaçant le fichier en place (conserve la propriété et les autorisations de foo, mais a besoin d'une protection contre les interruptions):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

Je recommande d'utiliser l'une des cutméthodes basées sur. De cette façon, vous ne dépendez d'aucun outil non standard, vous pouvez utiliser le meilleur outil pour le travail et vous contrôlez le comportement en cas d'interruption.

Gilles 'SO- arrête d'être méchant'
la source
Mieux que la .oldméthode pour les changements sur place,echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut
1
@GypsyCosmonaut Non, ce n'est pas «mieux». C'est plus fragile. Son seul avantage est qu'il est plus court à taper. Le principal problème avec votre méthode est que si une erreur se produit lors du traitement du fichier d'entrée ou de l'écriture de la sortie, les données sont perdues. Cela ne fonctionne pas non plus avec les données binaires: la sortie sera tronquée au premier octet nul. Même avec des fichiers texte, il supprime les lignes vides de la fin du fichier. Avec des fichiers volumineux, il peut échouer car les données doivent être stockées sous forme de chaîne dans la mémoire du shell (et rappelez-vous, si cela se produit, les données sont perdues).
Gilles 'SO- arrête d'être méchant'
oO Merci, je ne savais pas qu'il pouvait y avoir des problèmes avec l'octet nul
GypsyCosmonaut
Je pense que c'est plus simple:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh
@LoMaPh En effet, je ne sais pas pourquoi j'ai renommé l'ancien fichier: il n'a aucun avantage par rapport à renommer le nouveau fichier. C'est aussi plus simple car vous n'avez pas besoin de l'étape rm foo. Et vous ne devriez pas appeler rm foo, car il mv foo.new fooest atomique: il supprime l'ancienne version et met la nouvelle version en place en même temps.
Gilles 'SO- arrête d'être méchant'
23

Le paquet moreutils d'ubuntu (et aussi de debian ) a un programme appelé sponge, qui résout en quelque sorte votre problème.

De l'homme éponge:

l'éponge lit l'entrée standard et l'écrit dans le fichier spécifié. Contrairement à une redirection de shell, l'éponge absorbe toutes ses entrées avant d'ouvrir le fichier de sortie. Cela permet de restreindre les pipelines qui lisent et écrivent dans le même fichier.

Ce qui vous permettrait de faire quelque chose comme:

cut -d <delim> -f <fields> somefile | sponge somefile
Kjetil Jorgensen
la source
9

Je ne pense pas que ce soit possible en utilisant cutseul. Je ne pouvais pas le trouver dans la page man ou info. Vous pouvez faire quelque chose comme

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktempfait de vous un fichier temporaire relativement sûr dans lequel vous pouvez diriger la cutsortie.

Steven D
la source
1

Essayez vim-way:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

Cela modifiera le fichier sur place (faites donc la sauvegarde en premier).

Vous pouvez également utiliser grep, sedou gawk.

Kenorb
la source
0

Vous pouvez utiliser slurp avec POSIX Awk:

cut -b1 file | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' file

Exemple

Steven Penny
la source
0

Eh bien, puisque cutproduit moins de sortie qu'il n'en lit, vous pouvez faire:

cut -c1 < file 1<> file

C'est-à-dire, rendre son stdin fileouvert en mode lecture seule et son stdout fileouvert en mode lecture + écriture sans troncature ( <>).

De cette façon, cutécrasera simplement le fichier sur lui-même. Cependant, cela laissera le reste du fichier intact. Par exemple, si filecontient:

foo
bar

La sortie deviendra:

f
b
bar

L' f\nb\nont remplacé foo\n, mais barest toujours là. Vous devez tronquer le fichier une fois cutterminé.

Avec ksh93, vous pouvez le faire avec son <>;opérateur qui agit comme <>sauf que si la commande réussit, ftruncate()est appelée sur le descripteur de fichier. Donc:

cut -c1 < file 1<>; file

Avec d'autres obus, vous devez effectuer la ftruncate()via d'autres moyens comme:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

bien qu'invoquer perljuste pour cela est un peu exagéré ici, d'autant plus que cela perlpeut facilement faire ce cuttravail comme:

perl -pi -e '$_ = substr($_, 0, 1)' file

Attention, avec toutes les méthodes qui impliquent une réécriture réelle sur place, si l'opération est interrompue à mi-chemin, vous vous retrouverez avec un fichier corrompu. L'utilisation d'un deuxième fichier temporaire évite ce problème.

Stéphane Chazelas
la source