Comment puis-je supprimer la première ligne d'un fichier texte en utilisant le script bash/sed?

je dois à plusieurs reprises supprimer la première ligne d'un énorme fichier texte en utilisant un script bash.

en ce moment j'utilise sed -i -e "1d" $FILE - mais il faut environ une minute pour faire la suppression.

Est-il un moyen plus efficace pour accomplir cette?

425
demandé sur Peter Coulton 2008-12-04 05:50:16

14 réponses

Essayer GNU queue :

tail -n +2 "$FILE"

-n x : il suffit d'imprimer les dernières lignes x . tail -n 5 vous donnera les 5 dernières lignes de l'entrée. Le type de signe + inverse l'argument et fait tail imprimer n'importe quoi sauf les premières lignes x-1 . tail -n +1 imprimerait le dossier entier, tail -n +2 tout sauf la première ligne, etc.

GNU tail est beaucoup plus rapide que sed . tail est également disponible sur BSD et le drapeau -n +2 est cohérent pour les deux outils. Consultez les pages de manuel FreeBSD ou OS X pour plus d'informations.

la version BSD peut être beaucoup plus lente que sed , cependant. Je me demande comment ils ont géré cela; tail devrait simplement lire un fichier ligne par ligne tandis que sed fait des opérations assez complexes impliquant l'interprétation d'un script, l'application expressions régulières et similaires.

Note: vous pourriez être tenté d'utiliser

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

, mais cela vous donnera un fichier vide . La raison en est que la redirection ( > ) se produit avant que tail ne soit invoquée par le shell:

  1. Shell tronque le fichier $FILE
  2. Shell crée un nouveau procédé pour tail
  3. Shell redirige stdout du processus tail vers $FILE
  4. tail lit du Maintenant vide $FILE

Si vous souhaitez supprimer la première ligne à l'intérieur du fichier, vous devez utiliser:

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

le && veillera à ce que le fichier ne soit pas écrasé en cas de problème.

818
répondu Aaron Digulla 2016-08-25 07:20:48

vous pouvez utiliser-i pour mettre à jour le fichier sans utiliser l'opérateur'>'. La commande suivante va supprimer la première ligne du fichier et l'enregistrer dans le fichier.

sed -i '1d' filename
94
répondu amit 2015-10-21 03:55:14

pour ceux qui sont sur SunOS qui n'est pas GNU, le code suivant aidera:

sed '1d' test.dat > tmp.dat 
65
répondu Nasri Najib 2013-02-19 07:32:38

Non, c'est à peu près aussi efficace que vous allez obtenir. Vous pouvez écrire un programme C qui pourrait faire le travail un peu plus vite (moins de temps de démarrage et d'arguments de traitement) mais il aura probablement tendance à la même vitesse que sed que les fichiers deviennent gros (et je suppose qu'ils sont gros si cela prend une minute).

mais votre question souffre du même problème que tant d'autres en ce qu'elle présuppose la solution. Si vous nous dites en détail que vous essayez de faire plutôt que comment , nous pourrions être en mesure de Suggérer une meilleure option.

par exemple, s'il s'agit d'un fichier a qu'un autre programme b traite, une solution serait de ne pas rayer la première ligne, mais de modifier le programme B pour le traiter différemment.

disons que tous vos programmes ajoutent à ce fichier A et que le programme B Lit et traite actuellement la première ligne avant de la supprimer.

vous pourriez re-concevoir le programme B de sorte qu'il n'a pas essayé de supprimer la première ligne, mais maintient un offset persistant (probablement basé sur le fichier) dans le fichier A de sorte que, la prochaine fois qu'il exécute, il pourrait chercher à ce offset, traiter la ligne là, et mettre à jour le offset.

puis, à une heure calme (minuit?), il pourrait faire un traitement spécial du fichier a pour supprimer toutes les lignes actuellement traitées et remettre le décalage à 0.

ce sera certainement plus rapide pour qu'un programme ouvre et cherche un fichier plutôt que d'ouvrir et réécrire. Cette discussion suppose que vous avez le contrôle sur le programme B, Bien sûr. Je ne sais pas si c'est le cas, mais il pourrait y avoir d'autres solutions si vous fournissez plus d'information.

16
répondu paxdiablo 2008-12-04 03:19:12

vous can Éditez les fichiers en place: utilisez simplement le drapeau -i de perl, comme ceci:

perl -ni -e 'print unless $. == 1' filename.txt

Cela fait disparaître la première ligne, comme vous le demandez. Perl devra lire et copier le fichier entier, mais il prend des dispositions pour que la sortie soit sauvegardée sous le nom du fichier original.

9
répondu alexis 2013-02-15 21:40:27

comme Pax l'a dit, vous n'irez probablement pas plus vite que ça. La raison en est qu'il n'y a presque aucun système de fichiers qui supporte la troncature depuis le début du fichier, donc cela va être une opération O( n ) où n est la taille du fichier. Ce que vous pouvez faire beaucoup plus rapide est bien de remplacer la première ligne avec le même nombre d'octets (peut-être avec des espaces ou un commentaire) qui pourraient travailler pour vous en fonction de la exactement ce que vous êtes essayer de faire (qu'est-ce que c'est d'ailleurs?).

8
répondu Robert Gamble 2008-12-04 03:48:49

le sponge jusqu'à évite la nécessité de jongler avec un fichier temporaire:

tail -n +2 "$FILE" | sponge "$FILE"
5
répondu agc 2018-07-08 19:43:47

Que Diriez-vous d'utiliser csplit?

man csplit
csplit -k file 1 '{1}'
3
répondu Shahbaz 2012-06-01 09:44:48

devrait afficher les lignes sauf la première ligne:

cat textfile.txt | tail -n +2
3
répondu serup 2016-09-29 07:42:01

pourrait utiliser vim pour faire ceci:

vim -u NONE +'1d' +'wq!' /tmp/test.txt

cela devrait être plus rapide, puisque vim ne lira pas le fichier entier lors du processus.

2
répondu Hongbo Liu 2018-05-15 19:02:35

Si vous souhaitez modifier le fichier en place, vous pouvez toujours utiliser l'original ed au lieu de son s treaming successeur sed :

ed "$FILE" <<<$'1d\nwq\n'
1
répondu Mark Reed 2018-05-15 18:57:22

Puisqu'il semble que je ne peux pas accélérer la suppression, je pense qu'une bonne approche pourrait être de traiter le fichier en lots comme ceci:

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

l'inconvénient de ceci est que si le programme est tué au milieu (ou s'il y a un mauvais sql là - dedans-provoquant la partie" process " de mourir ou de bloquer), il y aura des lignes qui seront soit sautées, soit traitées deux fois.

(file1 contient des lignes de code sql)

0
répondu Brent 2008-12-04 03:40:33

Si ce que vous cherchez à faire est de récupérer après l'échec, vous pouvez simplement construire un fichier qui a ce que vous avez fait jusqu'à présent.

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done
0
répondu Tim 2017-02-01 15:53:48

est-ce que l'utilisation de tail sur les lignes N-1 et de diriger cela dans un fichier, suivi de supprimer l'ancien fichier, et de renommer le nouveau fichier à l'ancien nom faire le travail?

si je faisais cela programmatically, je lisais le fichier, et je me souviens de l'offset du fichier, après avoir lu chaque ligne, de sorte que je pouvais chercher de nouveau à cette position pour lire le fichier avec une ligne de moins dedans.

-1
répondu EvilTeach 2008-12-04 03:50:44