2009-10-20 1 views
5

ho ricevuto un file che ha una riga nel file in questo modo:Move (s) a seguire un'altra linea in un file

check=('78905905f5a4ed82160c327f3fd34cba') 

Mi piacerebbe essere in grado di spostare questa linea per seguire un linea che assomiglia a questo:

files=('somefile.txt') 

La matrice se a volte che possono estendersi più righe, per esempio:

files=('somefile.txt' 
     'file2.png' 
     'another.txt' 
     'andanother...') 

text 
in between 

check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

La matrice/linea alway s finisce in a) e nessun testo in mezzo conterrà una parentesi chiusa.

ho ottenuto alcuni consigli che awk può fare questo:

awk '/files/{ 
    f=0 
    print $0 
    for(i=1;i<=d;i++){ print a[i] } 
    g=0 
    delete a # remove array after found 
    next 
} 
/check/{ f=1; g=1 } 
f{ a[++d]=$0 } 
!g' file 

Questo sarà solo estendersi su una linea però. Mi è stato detto di espandere la ricerca:

awk '/source/ && /\)$/{ 
    f=0 
    print $0 
    for(i=1;i<=d;i++){ print a[i] } 
    g=0 
    delete a # remove array after found 
    next 
} 
/md5sum/ && /\)$/{ f=1; g=1 } 
f{ a[++d]=$0 } 
!g' 

Imparare semplicemente awk, quindi gradirei l'aiuto con questo. O se c'è un altro strumento che può farlo, mi piacerebbe saperlo. Qualcuno mi ha detto che "ed" questo tipo di funzionalità.

+0

Aha, necessità spostare le linee sia in alto che in basso, giusto? Ho rivisto la risposta qui sotto ... – DigitalRoss

risposta

2

Per rispondere alla tua ultima domanda prima, sì, awk è lo strumento tipico di Unix per questo, altri candidati sono l'incredibilmente potente Perl, Python, o .. il mio preferito .. Ruby. Uno dei vantaggi di awk è che è sempre lì; fa parte del sistema base. Un altro modo per risolvere questo tipo di problema è con uno script di editor che controlla ed(1) o ex(1).

Ok, nuovo programma per la domanda rivista. Questo programma sposterà le linee di "controllo" verso l'alto o verso il basso secondo necessità, in modo che seguano le linee "file".

BEGIN { 
    checkAt = 0 
    filesAt = 0 
    scanning = 0 
} 

/check=\(/ { 
    checkAt = NR 
    scanning = 1 
} 

/files=\(/ { 
    filesAt = NR 
    scanning = 1 
} 

/)$/ { 
    if (scanning) { 
    if (checkAt > filesAt) { 
     checkEnd = NR 
    } else { 
     filesEnd = NR 
    } 
    scanning = 0 
    } 
} 

{ 
    lines[NR] = $0 
} 

END { 
    for (i = 1; i <= NR; ++i) { 
    if (checkAt <= i && i <= checkEnd) { 
     continue 
    } 
    print lines[i] 
    if (i == filesEnd) { 
     for (j = checkAt; j <= checkEnd; ++j) { 
     print lines[j] 
     } 
    } 
    } 
} 
+0

Ehi, è grandioso, tuttavia la parentesi di chiusura viene troncata. ie check = (.... L'esempio che ho provato sull'array dei file era alla fine del file. Questo ha fatto la differenza? Inoltre, potrebbe essere fatto funzionare anche se l'array dei file è prima dell'array check:). Lo trovo diverso in alcuni file. –

+0

Ok, se aggiungi questa riga alla fine di 'mover.awk' si occuperà del caso in cui l'ultima cosa nel file è una linea di controllo():' END {per (v in salvataggio) {stampa salvata [ v]}} '* tuttavia * Non riesco a riprodurre il tuo) rapporto bug di troncamento. Puoi per favore rilasciare un test case su http://pastie.org (usa il tipo di file "plain text") ?? – DigitalRoss

+0

Ho inserito una versione aggiornata dello script in http://pastie.org/662905 Questa versione si occupa dell'ordinamento invertito eseguendo l'ultimo controllo se ne vede uno nuovo e producendo uno rimanente su EOF. Ma ho ancora bisogno di un test perché non riesco a riprodurre il bug. – DigitalRoss

0

Ecco come farlo con sed:

 
sed -e /^check=(/,/)/{H;d} -e /)/{G;s/\n//} < filename 

Ciò presuppone che non ci sono parentesi di destra dopo i "file = ..." Se ci sono, allora avrete bisogno di più precisione:

 
sed -e /^check=(/,/)/{H;d} -e /^files=(/,/)/{/)/{G;s/\n//}} < filename 

EDIT:
Lavorare in bash? Va bene, provate questo:

 
sed -e /^check=(/,/)/H -e /^check=(/,/)/d -e '/)/G;s/\n//' < filename 

Questo sembra funzionare, ma non è chiaro perché questa variante e non pochi altri quelli ovvi. Questo ballo di personaggi speciali è sempre un problema con le regex.

+1

Wow provando a fare con sed, un uomo coraggioso: P. Sì, ho provato questo con sed, ma i registri di comprensione non sono ancora arrivato a molto. Dal tuo comando sembra che bash stia cercando di interpretare la parentesi. Ho provato a sfuggire a loro ma sto ottenendo: sed: -e espressione # 1, carattere 0: non corrispondente '{' bash: d}: comando non trovato bash: s/n //}}: Nessun file o directory –

+0

senza fortuna. Usando gnu-sed 4.2.1 qui. bash: errore di sintassi vicino a un token imprevisto '( –

+0

* sigh * Se sei interessato potremmo fare alcuni esperimenti e farlo funzionare, ma dal momento che hai già una soluzione funzionante in awk sarebbe solo un exersize nell'apprendimento sed. – Beta

0

Ho cercato di farlo con Awk, ma sembrava che non avresti ottenuto nulla di intelligente, sarebbe stata solo la stessa logica, ma con un po 'di dolore Awk, e così ho fatto in Perl :)

#!/usr/bin/perl 

open(IN, $ARGV[0]) || die("Could not open file: " . $ARGV[0]); 

my $buffer=""; 

foreach $line (<IN>) { 
     if ($line =~ /^check=/) { 
       $flag = 1; 
       $buffer .= $line; 
     } elsif ($flag == 1 && $line =~/\)/) { 
       $flag = 0; 
       $buffer .= $line; 
     } elsif ($flag == 1) { 
       $buffer .= $line; 
     } elsif ($flag == 0 && $line =~ /^files=/) { 
       $flag = 2; 
       print $line; 
     } elsif ($flag == 2 && $line =~ /\)/) { 
       $flag = 0; 
       print $line; 
       if (length($buffer) > 0) { 
         print $buffer; 
         $buffer = ""; 
       } 
     } else { 
       print $line; 
     } 

} 

E l'uscita :)

Chill:~ rus$ cat test check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between 

files=('somefile.txt' 
     'file2.png' 
     'another.txt' 
     'andanother...') 

asdasdasd 

check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between 

files=('somefile.txt' 
     'file2.png' 
     'another.txt' 
     'andanother...') 

asdsd 

check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between 

files=('somefile.txt' 
     'file2.png' 
     'another.txt' 
     'andanother...') 

Chill:~ rus$ ./t.pl test 

text in between 

files=('somefile.txt' 
     'file2.png' 
     'another.txt' 
     'andanother...') check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

asdasdasd 


text in between 

files=('somefile.txt' 
     'file2.png' 
     'another.txt' 
     'andanother...') check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

asdsd 


text in between 

files=('somefile.txt' 
     'file2.png' 
     'another.txt' 
     'andanother...') check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

ta da?!: D

+0

urgh , la pasta in uscita è rovinata, ma credimi, funziona. Sono geloso delle soluzioni awk e sed però :) – RusHughes

+0

nah, questo è buono. Però non funziona per me. L'array di file viene cancellato e l'array di file esiste ancora. Ho ottenuto {e() chracters tra i due array, questo fa la differenza? –

+0

ho aggiunto un sacco di caratteri {()} ai miei dati di test e ha funzionato perfettamente! hai un esempio dei dati del test posso provarlo? – RusHughes

0

@todd, mi sembra di averti lasciato in balia dopo averti fornito la soluzione awk no. ? :). ecco un altro metodo, questa volta non usando il metodo dei flag. ci sono alcune estremità libere (suggerimento: controlla i modelli p, q e stampa di nuovo) che lascio a te per riordinare.

gawk 'BEGIN{ 
    RS="check=[(]" 
    q="files=(.*\047)" # pattern to replace files= part 
    p=".*(files=(.*\047)).*" # to get the whole files= part to variable 
} 
NR>1{ 
    b=gensub(p, "\\1","g",$0) # get the files=part to var b 
    printf "%s\n\n",b  
    printf "check=(" 
    gsub(q,"",$0) 
    print $0 
}' file 

NB: gensub è specifico per gawk quindi se avete gawk, allora che va bene

uscita

$ more file 
check=('5277a9164001a4276837b59dade26af2' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between one 

files=('somefile1.txt' 
     'file1.png'  
     'another1.txt' 
     'andanother1...') 

asdasdasd blah blah 

check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between two 

files=('somefile2.txt' 
     'file2.png'  
     'another2.txt' 
     'andanother2...') 

asdsd blaasdf aslasdfaslj aslfjsldfsa 123e12 

check=('78905905fblah blah5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2'   
     '3f8b60b6fbb993c18442b62ea661aa6b')   

text in between 

files=('somefile3.txt' 
     'file3.png'  
     'another3.txt' 
     'andanother3...') 

$ ./shell.sh 
files=('somefile1.txt'    
     'file1.png'     
     'another1.txt'    
     'andanother1...'    

check=('5277a9164001a4276837b59dade26af2' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between one 

) 

asdasdasd blah blah 


files=('somefile2.txt' 
     'file2.png' 
     'another2.txt' 
     'andanother2...' 

check=('78905905f5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between two 

) 

asdsd blaasdf aslasdfaslj aslfjsldfsa 123e12 


files=('somefile3.txt' 
     'file3.png' 
     'another3.txt' 
     'andanother3...' 

check=('78905905fblah blah5a4ed82160c327f3fd34cba' 
     '5277a9164001a4276837b59dade26af2' 
     '3f8b60b6fbb993c18442b62ea661aa6b') 

text in between 

) 
+0

Grazie fantasma. Sono stato a piangere in awk negli ultimi giorni e non ce l'ho ancora. Sto ancora imparando. Credo di essere il tipo che ama imparare su cosa e impararlo bene prima di andare avanti: D. Apprezzo l'aiuto, però, molto apprezzato. –

0

Questo potrebbe funzionare per voi:

sed ':a;$!N;/^files=.*\ncheck=/{/.*)$/!ba;s/\([^)]*)\)\(.*\)\(\ncheck=.*\)/\1\3\2/p;d};/^files=.*/ba;P;D' file