web-dev-qa-db-fra.com

Comment puis-je extraire une plage prédéterminée de lignes d'un fichier texte sous Unix?

J'ai un dump SQL de ~ 23000 lignes contenant plusieurs bases de données. Je dois extraire une certaine section de ce fichier (c'est-à-dire les données d'une base de données unique) et 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.

Est-ce que quelqu'un connaît une commande Unix (ou une série de commandes) pour extraire toutes les lignes d'un fichier entre la ligne 16224 et 16482, puis les rediriger vers un nouveau fichier?

447
Adam J. Forster
sed -n '16224,16482p;16483q' filename > newfile

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 que sed sort sans traiter plus commandes.

q - Quittez sed sans traiter d'autres commandes ou entrées . Notez que l'espace de motif actuel est imprimé si l'impression automatique n'est pas désactivée avec l'option -n.

et

Les adresses d'un script sed peuvent revêtir l'une des formes suivantes:

nombre Spécifier un numéro de ligne correspondra uniquement à 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 commençant par où la première adresse correspond, et continue jusqu'à la deuxième correspondances d'adresse (inclusivement).

681
boxxar
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.

195
JXG

Assez simple en utilisant la tête/queue:

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
78
manveru

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, vous 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. 

25
Mark Janssen

Il existe une autre approche avec awk:

awk 'NR==16224, NR==16482' file

Si le fichier est volumineux, il peut être bon de exit après avoir lu la dernière ligne souhaitée. De cette manière, le fichier ne sera pas lu inutilement avant la fin:

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file
20
fedorqui
Perl -ne 'print if 16224..16482' file.txt > new_file.txt
14
mmaibaum
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2
8
Cetra

sed -n '16224,16482p' < dump.sql

5
cubex
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 déterminer si vous souhaitez que le terme 'entre' inclue la ligne de fin ou non.

5
JP Lodine

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.

3
jan.vdbergh

J'étais sur le point de publier l'astuce tête/queue, mais en fait, je ne ferais probablement que lancer Emacs. ;-)

  1. esc-x aller à la ligne ret 16224
  2. marque (ctrl-space)
  3. esc-x aller à la ligne ret 16482
  4. esc-w

ouvrez le nouveau fichier de sortie, ctl-y save

Voyons ce qui se passe.

3
sammyo

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
2
Chinmoy Padhi

Utiliser Ruby:

Ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf
2
Carl Blakeley

J'utiliserais:

awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt

FNR contient le numéro d'enregistrement (ligne) de la ligne lue dans le fichier.

2
Paddy3118

J'ai écrit un programme Haskell appelé splitter qui fait exactement ceci: avoir un lire mon post de blog de publication .

Vous pouvez utiliser le programme comme suit:

$ cat somefile | splitter 16224-16482

Et c'est tout ce qu'il y a à faire. Vous aurez besoin de Haskell pour l'installer. Juste:

$ cabal install splitter

Et vous avez terminé. J'espère que vous trouvez ce programme utile.

2
Robert Massaioli

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
1
potong

J'ai écrit un petit script bash que vous pouvez exécuter à partir de votre ligne de commande, tant que vous mettez à jour votre PATH afin d'inclure son répertoire (ou que vous puissiez le placer dans un répertoire déjà contenu dans le PATH).

Utilisation: $ pinch nom du fichier début

#!/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
1
Nerdfighter

Je voulais faire la même chose à partir d'un script en utilisant une variable et je l'ai réalisé 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 voulais scinder une liste dans des dossiers distincts et j'ai trouvé la question initiale et sa réponse utiles. (La commande divisée n'est pas une option sur l'ancien OS, je dois porter le code).

1
KevinY

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

Imprimera la ligne [Data] 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 (mieux vaut ê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
0
Kemin Zhou

Debout sur les épaules de boxxar, j'aime bien ça:

sed -n '<first line>,$p;<last line>q' input

par exemple.

sed -n '16224,$p;16482q' input

Le $ signifie "dernière ligne". La première commande permet donc à sed d'imprimer toutes les lignes commençant par la ligne 16224 et la deuxième commande à sed quitter après en imprimant la ligne 16428. (L'ajout de 1 pour la gamme 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é que l’utilisation de $ n’a pas d’effets néfastes sur les performances.

0
Tilman Vogel

Le -n dans accepter les réponses fonctionne. Voici une autre façon au cas où vous êtes enclin.

cat $filename | sed "${linenum}p;d";

Cela fait ce qui suit:

  1. canaliser le contenu d'un fichier (ou insérer le texte comme vous le souhaitez).
  2. sed sélectionne la ligne donnée, l'imprime
  3. 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 $ {linum}} vous demandant de l'imprimer. Je suis à peu près sûr que le -n fait fondamentalement la même chose que le d ici.
0
ThinkBonobo