2015-07-06 17 views
12

Ho un file parse.txtinvertire un file nella shell Unix

parse.txt contiene le seguenti

remo/hello/1.0,remo/hello2/2.0,remo/hello3/3.0,whitney/hello/1.0,julie/hello/2.0,julie/hello/3.0 

e voglio il file output.txt come (per invertire l'ordine da ultimo a prima) utilizzando parse.txt

julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0 

ho provato il seguente codice:

tail -r parse.txt 

risposta

4

È possibile utilizzare questo comando awk:

awk -v RS=, '{a[++i]=$1} END{for (k=i; k>=1; k--) printf a[k] (k>1?RS:ORS)}' parse.txt 
julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0 
26

È possibile utilizzare il sorprendente tac da GNU Coreutils.

tac -s "," parse.txt > newparse.txt 

tac di default sarà "cat" il file sullo standard output, invertendo le righe. Specificando il separatore utilizzando il flag -s, è possibile semplicemente invertire i campi come desiderato.

(Potrebbe essere necessario fare una fase di post-elaborazione per ottenere le virgole per funzionare in modo corretto, che può essere un altro passo nella vostra pipeline.)

+5

Basta fare attenzione che la fine riga farà parte dell'ultimo campo se c'è uno –

+1

GRAZIE per la condizione che questo è parte di GNU Coreutils. La gente dimentica così spesso che non tutti usano Linux. – ghoti

+0

@EricRenouf - Esistono soluzioni alternative per questo? Ad esempio, supponiamo che invece di invertire una singola riga, OP avesse bisogno di invertire ogni riga di un file? –

2

È possibile utilizzare qualsiasi linguaggio per farlo

xargs ruby -e "puts ARGV[0].split(',').reverse.join(',')" < parse.txt 
+2

Avrai bisogno di 'chomp', e xargs sembra inutilmente complicato:' ruby ​​-ne 'mette $ _. chomp.split (","). reverse.join (",")' parse.txt' –

-1

L'inversione può essere fatta con tac (dal gatto). Come ha commentato questo invertirà le linee non quello che l'OP chiedeva.

tac filename 

è ancora possibile che si tac se fornire riga per riga e reverse da delimitatore non avanzamento riga, ma il separatore di campo, qui ,.

echo "a,b,c" | tr '\n' ',' | tac -s "," | sed 's/,$/\n/' 
+1

Il punto è di invertire i campi su ogni riga, non invertire le righe nel file. –

+0

@glennjackman Ci scusiamo per la risposta incompleta. le righe nei file sono determinate dal separatore, che è newline per impostazione predefinita. Modificando il separatore al delimitatore nel post è possibile ottenere l'inversione dei campi. – karakfa

9

mi piace la soluzione tac; è stretto ed elegante, ma come Micah ha sottolineato, tac fa parte di GNU Coreutils, il che significa che non è disponibile per impostazione predefinita in FreeBSD, OSX, Solaris, ecc

Questo può essere fatto in puro bash, senza strumenti esterni necessario.

#!/usr/bin/env bash 

unset comma 
read foo < parse.txt 
bar=(${foo//,/ }) 
for ((count="${#bar[@]}"; --count >= 0;)); do 
    printf "%s%s" "$comma" "${bar[$count]}" 
    comma="," 
done 

Questo ovviamente gestisce solo una linea, per l'input del campione. Puoi avvolgerlo in qualcosa se devi gestire più righe di input.

La logica è che possiamo convertire l'input in un array sostituendo le virgole con spazi. Naturalmente, se i nostri dati di input includessero spazi, questo dovrebbe essere regolato. Una volta ottenuto l'array, facciamo semplicemente un passo indietro, stampando ogni record.

Si noti che questo non include un nuovo fine di terminazione. Se volete uno, è possibile aggiungere con:

printf '\n' 

come una linea finale.

+1

Più semplice, sostituire 'printf '\ n'' con' echo' –

+1

Puoi sostituire le righe 'read' e' bar = 'con' IFS =, leggi -a barra

+0

@glennjackman, sai, io * so * che 'echo' si comporta in modo coerente finché sei in bash, ma anni fa ho perso l'abitudine di usarlo a causa di incongruenze in altre shell e sistemi operativi , e da allora ho continuato con 'printf'. Fortunatamente, sia "echo" che "printf" sono incorporati in bash, quindi non c'è un grave impatto sulle prestazioni. :) – ghoti

7
perl -F, -lane 'print join ",", reverse @F' parse.txt > output.txt 
4

La questione è aggiunto e hai menzionato tail -r che suggerisce che non potrebbero utilizzare Linux (con piena toolchain GNU), ma invece un po 'di "reale" Unix (variante BSD), ad esempio, .

Come tale, il comando tac non è disponibile, ma come indicato nella domanda, tail -r è. Così si può utilizzare il seguente:

$ tr ',' '\n' < parse.txt | tail -r | tr '\n' ',' | sed 's/,$//' 
julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0 
$ 

Note:

  • questo funziona solo per i file che hanno una linea, come ci affidiamo alla conversione virgole per ritorni a capo e schiena. Se è presente più di una riga, le nuove righe verranno convertite in virgola entro il secondo tr.
  • La finale sed è quello di rimuovere una virgola finale, che è stato convertito da un carattere di fine riga inserita da tail
2

Emulando tac con sed:

tr , '\n' <parse.txt | sed '1!G; h; $!d' | paste -sd , 

In alternativa, se non si dispone di paste :

tr , '\n' <parse.txt | sed '1!G; h; $!d' | tr '\n' , | sed 's/,$//' 

Uscita:

julie/hello/3.0,julie/hello/2.0,whitney/hello/1.0,remo/hello3/3.0,remo/hello2/2.0,remo/hello/1.0 
Problemi correlati