Comment puis-je extraire une plage prédéterminée de lignes d'un fichier texte sous Unix?
-
01-07-2019 - |
Question
J'ai un cliché SQL d'environ 23 000 lignes contenant plusieurs bases de données. J'ai besoin d'extraire une certaine section de ce fichier (c'est-à-dire les données d'une seule base de données) et de la placer dans un nouveau fichier. Je connais les numéros de ligne de début et de fin des données que je souhaite.
Quelqu'un connaît-il une commande Unix (ou une série de commandes) permettant d'extraire toutes les lignes d'un fichier compris entre la ligne 16224 et 16482, puis de les rediriger vers un nouveau fichier?
La solution
sed -n '16224,16482p;16483q' filename > newfile
Extrait du manuel sed :
p - Imprimez l'espace du modèle (sur la sortie standard). Cette commande est généralement utilisée uniquement avec l'option de ligne de commande -n.
n - Si l'impression automatique n'est pas désactivée, imprimez l'espace du motif, puis remplacez-le malgré tout par la ligne suivante. Si il n'y a pas plus d'entrée puis sed sort sans traitement plus commandes.
q - Quittez
sed
sans traiter plus de commandes ni d’entrée. Notez que l’espace motif actuel est imprimé si l’impression automatique n’est pas désactivée avec l’option -n.
Les adresses d'un script sed peuvent revêtir l'une des formes suivantes:
nombre Spécifier un numéro de ligne ne correspond qu'à cette ligne dans l'entrée.
Une plage d'adresses peut être spécifiée en spécifiant deux adresses. séparés par une virgule (,). Une plage d'adresses correspond aux lignes à partir de où la première adresse correspond, et continue jusqu'à la deuxième correspondances d'adresse (inclusivement).
Autres conseils
sed -n '16224,16482 p' orig-data-file > new-file
Où 16224,16482 sont le numéro de la ligne de début et le numéro de la ligne de fin inclus. Ceci est 1 indexé. -n
supprime l'écho de l'entrée en tant que sortie, ce que vous ne voulez manifestement pas; les nombres indiquent la plage de lignes sur laquelle la commande suivante doit être exécutée; la commande p
affiche les lignes appropriées.
Assez simple en utilisant head / tail:
head -16482 in.sql | tail -258 > out.sql
en utilisant sed:
sed -n '16482,16482p' in.sql > out.sql
en utilisant awk:
awk 'NR>=10&&NR<=20' in.sql > out.sql
Vous pouvez utiliser "vi" puis la commande suivante:
:16224,16482w!/tmp/some-file
Alternativement:
cat file | head -n 16482 | tail -n 258
EDIT: - Juste pour ajouter une explication, utilisez head -n 16482 pour afficher les 16482 premières lignes, puis utilisez tail -n 258 pour obtenir les 258 dernières lignes de la première sortie.
Il existe une autre approche avec awk
:
awk 'NR==16224, NR==16482' file
Si le fichier est volumineux, il peut être bon de quitter
après avoir lu la dernière ligne souhaitée. De cette manière, les lignes suivantes ne seront pas lues inutilement:
awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file
perl -ne 'print if 16224..16482' file.txt > new_file.txt
# print section of file based on line numbers
sed -n '16224 ,16482p' # method 1
sed '16224,16482!d' # method 2
sed -n '16224,16482p' < dump.sql
cat dump.txt | head -16224 | tail -258
devrait faire l'affaire. L'inconvénient de cette approche est que vous devez utiliser l'arithmétique pour déterminer l'argument de queue et pour déterminer si vous souhaitez que le terme 'entre' inclue la ligne de fin ou non.
Rapide et sale:
head -16428 < file.in | tail -259 > file.out
Ce n'est probablement pas la meilleure façon de le faire, mais cela devrait fonctionner.
BTW: 259 = 16482-16224 + 1.
J'ai écrit un programme Haskell appelé séparateur qui fait exactement cela: avoir un lisez mon billet de blog de sortie .
Vous pouvez utiliser le programme comme suit:
$ cat somefile | splitter 16224-16482
Et c’est tout ce qu’il ya à faire. Vous aurez besoin de Haskell pour l'installer. Juste:
$ cabal install splitter
Et vous avez terminé. J'espère que vous trouverez ce programme utile.
Même nous pouvons le faire pour vérifier en ligne de commande:
cat filename|sed 'n1,n2!d' > abc.txt
Par exemple:
cat foo.pl|sed '100,200!d' > abc.txt
Utilisation de ruby:
ruby -ne 'puts "#{$.}: #{ Utilisation de ruby:
<*>}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf
Debout sur les épaules de Boxxar, j'aime bien ceci:
sed -n '<first line>,$p;<last line>q' input
par exemple
sed -n '16224,$p;16482q' input
Le $
signifie "dernière ligne", donc la première commande permet à sed
d’imprimer toutes les lignes commençant par la ligne 16224
et la deuxième commande oblige sed
à quitter après la ligne d'impression 16428
. (L'ajout de 1
pour la plage q
dans la solution de boxxar ne semble pas nécessaire.)
J'aime cette variante car je n'ai pas besoin de spécifier le numéro de la ligne de fin deux fois. Et j’ai mesuré qu’utiliser $
n’avait pas d’effets néfastes sur les performances.
J'étais sur le point de publier le tour de tête / queue, mais en fait, je ne ferais probablement que lancer Emacs. ; -)
- échap - x ligne goto ret 16224
- marque ( ctrl - espace )
- écha - x ligne goto ret 16482
- échap - w
ouvrez le nouveau fichier de sortie, ctl-y enregistrer
Voyons ce qui se passe.
Je voudrais utiliser:
awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt
FNR contient le numéro d'enregistrement (ligne) de la ligne lue dans le fichier.
J'ai écrit un petit script bash que vous pouvez exécuter à partir de votre ligne de commande, à condition de mettre à jour votre chemin PATH afin d'inclure son répertoire (ou de le placer dans un répertoire déjà contenu dans le chemin PATH).
Utilisation: $ pinch nom_fichier ligne de début ligne de fin
#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon
FILENAME=$1
START=$2
END=$3
ERROR="[PINCH ERROR]"
# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
echo "$ERROR Need three arguments: Filename Start-line End-line"
exit 1
fi
# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
echo -e "$ERROR File does not exist. \n\t$FILENAME"
exit 1
fi
# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
echo -e "$ERROR Start line is greater than End line."
exit 1
fi
# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
echo -e "$ERROR Start line is less than 0."
exit 1
fi
# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
echo -e "$ERROR End line is less than 0."
exit 1
fi
NUMOFLINES=$(wc -l < "$FILENAME")
# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
echo -e "$ERROR End line is greater than number of lines in file."
exit 1
fi
# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))
# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
< "$FILENAME" head -n $END | tail -n +$START
else
< "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi
# Success
exit 0
Cela pourrait fonctionner pour vous (GNU sed):
sed -ne '16224,16482w newfile' -e '16482q' file
ou en profitant de bash:
sed -n 16224,16482w newfile\n16482q' file
Je voulais faire la même chose à partir d'un script en utilisant une variable et y parvenir en mettant des guillemets autour de la variable $ pour séparer le nom de la variable du p:
sed -n "$first","$count"p imagelist.txt >"$imageblock"
Je souhaitais scinder une liste en plusieurs dossiers et trouvais la question initiale et la réponse utiles. (la commande divisée n'est pas une option sur l'ancien système d'exploitation auquel je dois porter le code).
Le -n dans les réponses acceptées fonctionne. Voici un autre moyen au cas où vous seriez enclin.
cat $filename | sed "${linenum}p;d";
Cela fait ce qui suit:
- canaliser le contenu d'un fichier (ou insérer le texte comme vous le souhaitez).
- sed sélectionne la ligne donnée, l’affiche
- d est nécessaire pour supprimer des lignes, sinon sed supposera que toutes les lignes seront finalement imprimées. c'est-à-dire que sans le d, toutes les lignes imprimées par la ligne sélectionnée seront imprimées deux fois, car vous avez la partie $ {Linnum} p qui demande à être imprimée. Je suis à peu près sûr que le -n fait fondamentalement la même chose que le d ici.
Puisqu'on parle d'extraire des lignes de texte d'un fichier texte, je vais vous donner un cas particulier dans lequel vous souhaitez extraire toutes les lignes qui correspondent à un certain motif.
myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile
Imprime la ligne [Données] et le reste. Si vous voulez le texte de line1 dans le motif, vous tapez: sed -n '1, / Data / p' myfile. De plus, si vous connaissez deux modèles (il est préférable d’être unique dans votre texte), les lignes de début et de fin de la plage peuvent être spécifiées avec des correspondances.
sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
Je pense que cela pourrait être une solution utile. Si le nom de la table est " personne " Vous pouvez utiliser sed pour obtenir toutes les lignes nécessaires à la restauration de votre table.
sed -n -e '/DROP TABLE IF EXISTS.*`person `/,/UNLOCK TABLES/p' data.sql > new_data.sql
Sur la base de cette réponse , où il manque le champ DROP TABLE IF EXIST " pour la table que vous restaurez et vous devez supprimer quelques lignes du bas du nouveau fichier avant de l’utiliser pour éviter de supprimer la table suivante.
Des informations détaillées sont également disponibles ici