web-dev-qa-db-fra.com

Ajoutez des lignes au début et à la fin de l'énorme fichier

J'ai le scénario où des lignes doivent être ajoutées au début et à la fin des fichiers énormes.

J'ai essayé comme indiqué ci-dessous.

  • pour la première ligne:

    sed -i '1i\'"$FirstLine" $Filename
    
  • pour la dernière ligne:

    sed -i '$ a\'"$Lastline" $Filename  
    

Mais le problème avec cette commande est qu'elle ajoute la première ligne du fichier et traverse le fichier entier. Pour la dernière ligne, il traverse à nouveau l'intégralité du fichier et ajoute une dernière ligne. Depuis son très gros fichier (14 Go), cela prend beaucoup de temps.

Comment puis-je ajouter une ligne au début et une autre à la fin d'un fichier en ne lisant le fichier qu'une seule fois?

24
UNIXbest

sed -i utilise les fichiers temporaires comme détail d'implémentation, c'est ce que vous vivez; cependant, ajouter des données au début d'un flux de données sans écraser le contenu existant nécessite de réécrire le fichier, il n'y a aucun moyen de contourner cela, même en évitant sed -i.

Si la réécriture du fichier n'est pas une option, vous pouvez envisager de le manipuler lors de sa lecture, par exemple:

{ echo some prepended text ; cat file ; } | command

De plus, sed sert à éditer des flux - un fichier n'est pas un flux. Utilisez un programme conçu à cet effet, comme ed ou ex. Le -i l'option de sed n'est pas seulement non portable, elle cassera également tous les liens symboliques vers votre fichier, car elle le supprime essentiellement et le recrée, ce qui est inutile.

Vous pouvez le faire dans une seule commande avec ed comme ceci:

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

Notez que selon votre implémentation de ed, il peut utiliser un fichier d'échange, vous obligeant à avoir au moins autant d'espace disponible.

22
Chris Down

Notez que si vous voulez éviter d'allouer une copie entière du fichier sur le disque, vous pouvez faire:

sed '
1i\
begin
$a\
end' < file 1<> file

Cela utilise le fait que lorsque son stdin/stdout est un fichier, sed lit et écrit par bloc. Donc ici, c'est OK pour qu'il écrase le fichier qu'il lit tant que la première ligne que vous ajoutez est plus petite que la taille de bloc de sed (devrait être quelque chose comme 4k ou 8k).

Notez cependant que si pour une raison quelconque sed échoue (tué, plantage de la machine ...), vous vous retrouverez avec le fichier à moitié traité, ce qui signifiera que certaines données de la taille de la première ligne manquent quelque part au milieu .

Notez également que si votre sed n'est pas le GNU sed, cela ne fonctionnera pas pour les données binaires (mais puisque vous utilisez -i, vous utilisez GNU sed).

9
Stéphane Chazelas

Voici quelques choix (qui créeront tous une nouvelle copie du fichier, alors assurez-vous d'avoir suffisamment d'espace pour cela):

  • écho/chat simple

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
    
  • awk/gawk etc

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 
    

    awk et ses autres fichiers de lecture ligne par ligne. Le BEGIN{} le bloc est exécuté avant la première ligne et le END{} bloquer après la dernière ligne. Ainsi, la commande ci-dessus signifie print "first" at the beginning, then print every line in the file and print "last" at the end.

  • Perl

    Perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile
    

    C'est essentiellement la même chose que le gawk ci-dessus qui vient d'être écrit en Perl.

4
terdon

Je préfère le plus simple:

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

Cela transforme le fichier:

asdf
qwer

au fichier:

foo
asdf
qwer
bar
3
CommaToast

Il n'y a aucun moyen d'insérer des données au début d'un fichier¹, tout ce que vous pouvez faire est de créer un nouveau fichier, d'écrire les données supplémentaires et d'ajouter les anciennes données. Vous devrez donc réécrire tout le fichier au moins une fois pour insérer la première ligne. Vous pouvez cependant ajouter la dernière ligne sans réécrire le fichier.

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

Alternativement, vous pouvez combiner les deux commandes en une seule exécution de sed.

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

sed -i crée un nouveau fichier de sortie, puis le déplace sur l'ancien fichier. Cela signifie que pendant que sed fonctionne, il existe une deuxième copie du fichier utilisant de l'espace. Vous pouvez éviter cela en écrasant le fichier en place , mais avec des restrictions majeures: la ligne que vous ajoutez doit être plus petite que le tampon de sed, et si votre système plante, vous vous retrouverez avec un fichier endommagé fichier et du contenu perdu au milieu, donc je le déconseille fortement.

¹ Linux a un moyen d'insérer des données dans un fichier, mais il ne peut insérer qu'un nombre entier de blocs de système de fichiers, il ne peut pas insérer de chaînes de longueurs arbitraires. Il est utile pour certaines applications, telles que les bases de données et les machines virtuelles, mais il est inutile pour les fichiers texte.

Vous pouvez utiliser Vim en mode Ex:

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 sélectionner la première ligne

  2. i insérer du texte et une nouvelle ligne

  3. $ sélectionner la dernière ligne

  4. a ajout de texte et de nouvelle ligne

  5. x enregistrer et fermer

2
Steven Penny
$ (echo "Some Text" ; cat file1) > file2
0
Koushik Karmakar