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?
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:
$FILE
tail
tail
vers $FILE
tail
lit le $FILE
maintenant videSi 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.
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
Pour ceux qui utilisent SunOS, qui n'est pas GNU, le code suivant vous aidera:
sed '1d' test.dat > tmp.dat
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.
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.
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?).
Le sponge
util évite de jongler avec un fichier temporaire:
tail -n +2 "$FILE" | sponge "$FILE"
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'
Que diriez-vous d'utiliser csplit?
man csplit
csplit -k file 1 '{1}'
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.
devrait montrer les lignes sauf la première ligne:
cat textfile.txt | tail -n +2
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
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)
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>