web-dev-qa-db-fra.com

Comment puis-je supprimer la première ligne d'un fichier texte à l'aide du script bash/sed?

J'ai besoin de supprimer à plusieurs reprises la première ligne d'un énorme fichier texte à l'aide d'un script bash.

Pour le moment, j'utilise sed -i -e "1d" $FILE - mais la suppression prend environ une minute.

Existe-t-il un moyen plus efficace d'y parvenir?

449
Brent

Essayez (GNU tail } _:

tail -n +2 "$FILE"

-n x: Imprimez simplement les dernières lignes x. tail -n 5 vous donnerait les 5 dernières lignes de l'entrée. Le type de signe + inverse l'argument et fait en sorte que tail imprime autre chose que les premières lignes x-1. tail -n +1 imprimerait le fichier 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 l'indicateur -n +2 est cohérent dans les deux outils. Consultez les pages de manuel FreeBSD _ ou OS X pour plus d’informations. 

La version BSD peut toutefois être beaucoup plus lente que sed. Je me demande comment ils ont géré ça. tail devrait simplement lire un fichier ligne par ligne pendant que sed effectuait des opérations assez complexes impliquant l'interprétation d'un script, l'application d'expressions régulières, etc.

Remarque: vous pouvez ê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 appelé par le shell:

  1. Shell tronque le fichier $FILE
  2. Shell crée un nouveau processus pour tail
  3. Shell redirige la sortie standard du processus tail vers $FILE
  4. tail lit le $FILE maintenant vide

Si vous souhaitez supprimer la première ligne du fichier, vous devez utiliser:

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

Le && fera en sorte que le fichier ne soit pas écrasé en cas de problème.

866
Aaron Digulla

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

sed -i '1d' filename
119
amit

Pour ceux qui utilisent SunOS, qui n'est pas GNU, le code suivant vous aidera:

sed '1d' test.dat > tmp.dat 
67
Nasri Najib

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

Mais votre question souffre du même problème que tant d’autres, en ce sens qu’elle présuppose la solution. Si vous deviez nous dire en détail ce que vous essayez de faire plutôt que comment , nous pourrions peut-être suggérer une meilleure option.

Par exemple, s'il s'agit d'un fichier A traité par un autre programme B, une solution serait de ne pas supprimer la première ligne, mais de modifier le programme B pour le traiter différemment.

Supposons que tous vos programmes soient ajoutés à ce fichier A et que le programme B lit et traite actuellement la première ligne avant de le supprimer.

Vous pouvez réorganiser le programme B de sorte qu'il n'essaye pas de supprimer la première ligne mais conserve un décalage persistant (probablement basé sur un fichier) dans le fichier A afin que, lors de son prochain lancement, il puisse rechercher cet offset, processus la ligne là-bas, et mettre à jour le décalage.

Ensuite, à une heure calme (minuit?), Il pourrait effectuer un traitement spécial du fichier A pour supprimer toutes les lignes en cours de traitement et remettre le décalage à 0.

Il sera certainement plus rapide pour un programme d'ouvrir et de rechercher un fichier plutôt que d'ouvrir et de réécrire. Cette discussion suppose que vous ayez le contrôle du programme B, bien sûr. Je ne sais pas si c'est le cas, mais d'autres informations sont possibles.

16
paxdiablo

Vous pouvez éditez les fichiers à la 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 l'intégralité du fichier, mais il organisera l'enregistrement de la sortie sous le nom du fichier d'origine.

9
alexis

Comme Pax l'a dit, vous n'allez probablement pas aller plus vite que cela. La raison en est qu’il n’existe pratiquement aucun système de fichiers prenant en charge la troncature à partir du début du fichier; il s’agit donc d’une opération O (n) où n est la taille du fichier. Ce que vous pouvez faire beaucoup plus rapidement, c’est d’écraser la première ligne avec le même nombre d’octets (peut-être avec des espaces ou un commentaire), ce qui pourrait fonctionner pour vous en fonction de ce que vous essayez de faire (qu'est-ce que au fait?).

8
Robert Gamble

Le sponge util évite de jongler avec un fichier temporaire:

tail -n +2 "$FILE" | sponge "$FILE"
6
agc

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

ed "$FILE" <<<$'1d\nwq\n'
4
Mark Reed

Que diriez-vous d'utiliser csplit?

man csplit
csplit -k file 1 '{1}'
3
crydo

Vim pourrait utiliser ceci:

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

Cela devrait être plus rapide, car vim ne lira pas le fichier entier lors du traitement.

3
Hongbo Liu

devrait montrer les lignes sauf la première ligne:

cat textfile.txt | tail -n +2
2
serup

Si vous cherchez à récupérer après une défaillance, vous pouvez simplement créer un fichier contenant 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
Tim

Comme il semble que je ne puisse pas accélérer la suppression, je pense qu'une bonne approche pourrait consister à traiter le fichier par lots de la manière suivante:

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

L'inconvénient est que si le programme est tué au milieu (ou s'il y a du mauvais SQL dans celui-ci - provoquant la mort ou le blocage de la partie "processus"), il y aura des lignes qui seront ignorées ou traitées deux fois. .

(fichier1 contient des lignes de code SQL)

0
Brent

Vous pouvez facilement le faire avec:

cat filename | sed 1d > filename_without_first_line

sur la ligne de commande; ou pour supprimer définitivement la première ligne d'un fichier, utilisez le mode de remplacement de sed avec l'indicateur -i:

sed -i 1d <filename>
0
Ingo Baab