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

J'ai un vidage SQL de ligne ~ 23000 contenant plusieurs bases de données d'une valeur de données. J'ai besoin d'extraire une partie de ce fichier (c'est à dire les données d'une base de données unique) et le placer dans un nouveau fichier. Je connais à la fois les numéros de ligne de début et de fin des données que je veux.

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 dans un nouveau fichier?

422
demandé sur lesmana 2008-09-17 17:40:59

21 réponses

sed -n '16224,16482p;16483q' filename > newfile

Du manuel sed :

P - Imprimer l'espace de motif (à 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 de motif, puis remplacez-le par la ligne d'entrée suivante. Si il n'y a plus d'entrée puis sed quitte sans traitement de plus commande.

Q - Exit sed sans traiter plus de commandes ou d'entrée. 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 dans un script sed peuvent être sous l'une des formes suivantes:

Nombre La spécification d'un numéro de ligne correspond seulement 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 seconde correspond à l'adresse (inclusivement).

635
répondu boxxar 2018-06-13 18:09:58
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, inclusivement. C'est 1-indexé. -n supprime l'écho de l'entrée comme sortie, ce que vous ne voulez clairement pas; les chiffres indiquent la plage de lignes pour faire fonctionner la commande suivante; la commande p imprime les lignes pertinentes.

184
répondu JXG 2010-10-19 10:55:41

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
73
répondu manveru 2008-09-17 13:46:05

Vous pouvez utiliser ' vi ' puis la commande suivante:

:16224,16482w!/tmp/some-file

Sinon:

cat file | head -n 16482 | tail -n 258

EDIT:- il suffit d'ajouter une explication, vous utilisez head-n 16482 pour afficher en premier 16482 lignes puis utilisez tail-n 258 pour obtenir les dernières 258 lignes de la première sortie.

23
répondu Mark Janssen 2016-07-16 20:37:10

Il y a une autre approche avec awk:

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

Si le fichier est énorme, il peut être bon de exit Après avoir lu la dernière ligne désirée. De cette façon, il ne lira pas inutilement le fichier jusqu'à la fin:

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file
19
répondu fedorqui 2014-01-15 09:29:40
perl -ne 'print if 16224..16482' file.txt > new_file.txt
13
répondu mmaibaum 2015-01-05 18:38:10
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2
7
répondu Cetra 2008-09-17 13:42:38

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

5
répondu cubex 2008-09-17 13:45:18
cat dump.txt | head -16224 | tail -258

Devrait faire l'affaire. L'inconvénient de cette approche est que vous devez faire l'arithmétique pour déterminer l'argument de la queue et pour tenir compte si vous voulez que le 'between' inclue la ligne de fin ou non.

5
répondu JP Lodine 2015-01-05 18:39:48

Rapide et sale:

head -16428 < file.in | tail -259 > file.out

Probablement pas la meilleure façon de le faire, mais il devrait fonctionner.

BTW: 259 = 16482-16224 + 1.

3
répondu jan.vdbergh 2008-09-17 13:44:24

J'étais sur le point de poster le tour tête/queue, mais en fait je ferais probablement juste lancer emacs. ;-)

  1. esc-x goto-line ret 16224
  2. marque (ctrl-l'espace)
  3. esc-x goto-line ret 16482
  4. ces-w

Ouvre le nouveau fichier de sortie, ctl-y enregistrer

Voyons ce qui se passe.

2
répondu sammyo 2010-05-26 15:40:09

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.

2
répondu Paddy3118 2013-01-01 18:51:30

J'ai écrit un programme Haskell appelé splitter, ce qui fait exactement cela: avoir un lire ma version blog.

Vous pouvez utiliser le programme comme suit:

$ cat somefile | splitter 16224-16482

Et c'est tout ce qu'il y a à cela. 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.

2
répondu Robert Massaioli 2013-07-25 22:43:32

Même, nous pouvons le faire pour vérifier à la ligne de commande:

cat filename|sed 'n1,n2!d' > abc.txt

Par Exemple:

cat foo.pl|sed '100,200!d' > abc.txt
2
répondu Chinmoy Padhi 2014-02-05 07:02:03

En utilisant ruby:

ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf
2
répondu Carl Blakeley 2015-05-21 12:23:02

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

Utilisation: $ pincée de nom de fichier de début de ligne fin de ligne

#!/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
répondu Nerdfighter 2014-12-10 17:06:47

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
répondu potong 2015-08-10 13:00:01

Je voulais faire la même chose à partir d'un script utilisant une variable et 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 diviser une liste en dossiers séparés et j'ai trouvé la question initiale et la réponse une étape utile. (la commande split n'est pas une option sur l'ancien système d'exploitation auquel je dois porter le code).

1
répondu KevinY 2017-10-28 09:35:10

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

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

Cela fait ce qui suit:

  1. conduit dans le contenu d'un fichier (ou alimente 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, sans le d, vous obtiendrez toutes les lignes imprimées par la ligne sélectionnée imprimée deux fois parce que vous avez la partie ${linenum}p en demandant qu'il soit imprimé. Je suis sûr que le-n fait essentiellement la même chose que le d ici.
0
répondu ThinkBonobo 2016-01-07 18:52:53

Puisque nous parlons d'extraire des lignes de texte à partir d'un fichier texte, je vais donner un cas particulier où vous voulez 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 que le texte de line1 au motif, vous tapez: sed - n '1,/Data / P' myfile. De plus, si vous connaissez deux motifs (mieux vaut être unique dans votre texte), la ligne de début et de fin de la plage peut être spécifiée avec des correspondances.

sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
0
répondu Kemin Zhou 2018-02-14 22:52:13

Je pense que cela pourrait être utile. Si le nom de la table est "person", vous pouvez utiliser sed pour obtenir toutes les lignes dont vous avez besoin pour restaurer votre table.

sed -n -e '/DROP TABLE IF EXISTS.*`person `/,/UNLOCK TABLES/p' data.sql  > new_data.sql

Basé sur cette réponse , où il manque le "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 empêcher la suppression de la table suivante.

Des informations Détaillées peuvent également être trouvés ici

-3
répondu dvergur 2017-05-23 12:02:48