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?
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:
- Shell tronque le fichier
$FILE
- Shell crée un nouveau procédé pour
tail
- Shell redirige stdout du processus
tail
vers$FILE
-
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.
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
pour ceux qui sont sur SunOS qui n'est pas GNU, le code suivant aidera:
sed '1d' test.dat > tmp.dat
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.
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.
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?).
le sponge
jusqu'à évite la nécessité de jongler avec un fichier temporaire:
tail -n +2 "$FILE" | sponge "$FILE"
Que Diriez-vous d'utiliser csplit?
man csplit
csplit -k file 1 '{1}'
devrait afficher les lignes sauf la première ligne:
cat textfile.txt | tail -n +2
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.
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'
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)
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
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.