web-dev-qa-db-fra.com

Diviser un fichier en plusieurs fichiers en fonction du délimiteur

J'ai un fichier avec -| comme séparateur après chaque section ... nécessité de créer des fichiers séparés pour chaque section en utilisant Unix.

exemple de fichier d'entrée

wertretr
ewretrtret
1212132323
000232
-|
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|

Résultat attendu dans le fichier 1

wertretr
ewretrtret
1212132323
000232
-|

Résultat attendu dans le fichier 2

ereteertetet
232434234
erewesdfsfsfs
0234342343
-|

Résultat attendu dans le fichier 3

jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
63
user1499178

Un seul paquebot, pas de programmation. (sauf l'expression rationnelle, etc.)

csplit --digits=2  --quiet --prefix=outfile infile "/-|/+1" "{*}"
78
ctrl-alt-delor
awk '{print $0 " -|"> "file" NR}' RS='-\\|'  input-file

Explication (édité): 

RS est le séparateur d’enregistrements et cette solution utilise une extension gnu awk qui lui permet d’avoir plus d’un caractère. NR est le numéro d'enregistrement. 

L'instruction print imprime un enregistrement suivi de " -|" dans un fichier contenant le numéro d'enregistrement dans son nom.

29
William Pursell

Debian a csplit, mais je ne sais pas si cela est commun à toutes/la plupart/d'autres distributions. Sinon, il ne devrait pas être trop difficile de localiser la source et de la compiler ...

7
twalberg

J'ai résolu un problème légèrement différent, où le fichier contient une ligne avec le nom où le texte qui suit doit être placé. Ce code Perl fait le tour pour moi: 

#!/path/to/Perl -w

#comment the line below for UNIX systems
use Win32::Clipboard;

# Get command line flags

#print ($#ARGV, "\n");
if($#ARGV == 0) {
    print STDERR "usage: ncsplit.pl --mff -- filename.txt [...] \n\nNote that no space is allowed between the '--' and the related parameter.\n\nThe mff is found on a line followed by a filename.  All of the contents of filename.txt are written to that file until another mff is found.\n";
    exit;
}

# this package sets the ARGV count variable to -1;

use Getopt::Long;
my $mff = "";
GetOptions('mff' => \$mff);

# set a default $mff variable
if ($mff eq "") {$mff = "-#-"};
print ("using file switch=", $mff, "\n\n");

while($_ = shift @ARGV) {
    if(-f "$_") {
    Push @filelist, $_;
    } 
}

# Could be more than one file name on the command line, 
# but this version throws away the subsequent ones.

$readfile = $filelist[0];

open SOURCEFILE, "<$readfile" or die "File not found...\n\n";
#print SOURCEFILE;

while (<SOURCEFILE>) {
  /^$mff (.*$)/o;
    $outname = $1;
#   print $outname;
#   print "right is: $1 \n";

if (/^$mff /) {

    open OUTFILE, ">$outname" ;
    print "opened $outname\n";
    }
    else {print OUTFILE "$_"};
  }
5
John David Smith

Vous pouvez également utiliser awk. Je ne connais pas très bien awk, mais ce qui suit a semblé fonctionner pour moi. Il a généré part1.txt, part2.txt, part3.txt et part4.txt. Notez que le dernier fichier partn.txt que cela génère est vide. Je ne sais pas comment résoudre ce problème, mais je suis sûr que cela pourrait être fait avec un peu de peaufinage. Des suggestions quelqu'un?

Fichier awk_pattern:

BEGIN{ fn = "part1.txt"; n = 1 }
{
   print > fn
   if (substr($0,1,2) == "-|") {
       close (fn)
       n++
       fn = "part" n ".txt"
   }
}

commande bash:

awk -f awk_pattern input.file

2
rkyser

Utilisez csplit si vous en avez. 

Si vous ne le faites pas, mais que vous avez Python ... n'utilisez pas Perl.

Lecture paresseuse du dossier

Votre fichier est peut-être trop volumineux pour pouvoir être conservé en mémoire en une seule fois - la lecture ligne par ligne peut être préférable. Supposons que le fichier d'entrée s'appelle "samplein":

$ python3 -c "from itertools import count
with open('samplein') as file:
    for i in count():
        firstline = next(file, None)
        if firstline is None:
            break
        with open(f'out{i}', 'w') as out:
            out.write(firstline)
            for line in file:
                out.write(line)
                if line == '-|\n':
                    break"
1
Aaron Hall

La commande suivante fonctionne pour moi. J'espère que ça aide.

awk 'BEGIN{file = 0; filename = "output_" file ".txt"}
    /-|/ {getline; file ++; filename = "output_" file ".txt"}
    {print $0 > filename}' input
1
Thanh

Voici un script Python 3 qui divise un fichier en plusieurs fichiers en fonction d'un nom de fichier fourni par les délimiteurs. Exemple de fichier d'entrée:

# Ignored

######## FILTER BEGIN foo.conf
This goes in foo.conf.
######## FILTER END

# Ignored

######## FILTER BEGIN bar.conf
This goes in bar.conf.
######## FILTER END

Voici le script:

#!/usr/bin/env python3

import os
import argparse

# global settings
start_delimiter = '######## FILTER BEGIN'
end_delimiter = '######## FILTER END'

# parse command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input-file", required=True, help="input filename")
parser.add_argument("-o", "--output-dir", required=True, help="output directory")

args = parser.parse_args()

# read the input file
with open(args.input_file, 'r') as input_file:
    input_data = input_file.read()

# iterate through the input data by line
input_lines = input_data.splitlines()
while input_lines:
    # discard lines until the next start delimiter
    while input_lines and not input_lines[0].startswith(start_delimiter):
        input_lines.pop(0)

    # corner case: no delimiter found and no more lines left
    if not input_lines:
        break

    # extract the output filename from the start delimiter
    output_filename = input_lines.pop(0).replace(start_delimiter, "").strip()
    output_path = os.path.join(args.output_dir, output_filename)

    # open the output file
    print("extracting file: {0}".format(output_path))
    with open(output_path, 'w') as output_file:
        # while we have lines left and they don't match the end delimiter
        while input_lines and not input_lines[0].startswith(end_delimiter):
            output_file.write("{0}\n".format(input_lines.pop(0)))

        # remove end delimiter if present
        if not input_lines:
            input_lines.pop(0)

Enfin voici comment vous l'exécutez:

$ python3 script.py -i input-file.txt -o ./output-folder/
1
ctrlc-root

Voici un code Perl qui fera l'affaire

#!/usr/bin/Perl
open(FI,"file.txt") or die "Input file not found";
$cur=0;
open(FO,">res.$cur.txt") or die "Cannot open output file $cur";
while(<FI>)
{
    print FO $_;
    if(/^-\|/)
    {
        close(FO);
        $cur++;
        open(FO,">res.$cur.txt") or die "Cannot open output file $cur"
    }
}
close(FO);
0
amaksr
cat file| ( I=0; echo -n "">file0; while read line; do echo $line >> file$I; if [ "$line" == '-|' ]; then I=$[I+1]; echo -n "" > file$I; fi; done )

et la version formatée:

#!/bin/bash
cat FILE | (
  I=0;
  echo -n"">file0;
  while read line; 
  do
    echo $line >> file$I;
    if [ "$line" == '-|' ];
    then I=$[I+1];
      echo -n "" > file$I;
    fi;
  done;
)
0
mbonnin