Risposte:
Per la cut(1)pagina man:
Usa uno e solo uno di -b, -c o -f. Ogni ELENCO è composto da un intervallo o da molti intervalli separati da virgole. L'input selezionato viene scritto nello stesso ordine in cui viene letto e viene scritto esattamente una volta.
Raggiunge prima il campo 1, quindi viene stampato, seguito dal campo 2.
Usa awkinvece:
awk '{ print $2 " " $1}' file.txt
FSè un'opzione, OFSè una variabile. es.awk -v OFS=";" -F"\t" '{print $2,$1}'
| sed 's/\r//' | prima di eseguire il piping aawk
awk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
Puoi anche combinare cute paste:
paste <(cut -f2 file.txt) <(cut -f1 file.txt)
tramite commenti: è possibile evitare i bashismi e rimuovere un'istanza di taglio facendo:
paste file.txt file.txt | cut -f2,3
cutfunziona bene per colonne di lunghezza variabile purché si disponga di un separatore di colonne univoco.
bashismi e rimuovere un'istanza cutfacendo: paste file.txt file.txt | cut -f2,3
usando solo la shell,
while read -r col1 col2
do
echo $col2 $col1
done <"file"
"$col2"e "$col1"- potrebbero esserci metacaratteri della shell o altri shenanigans nei dati.
Puoi usare Perl per questo:
perl -ane 'print "$F[1] $F[0]\n"' < file.txt
Il vantaggio di eseguire perl è che (se conosci Perl) puoi fare molto più calcolo su F che riorganizzare le colonne.
perl -ae printfunziona come catper me
Utilizzando join:
join -t $'\t' -o 1.2,1.1 file.txt file.txt
Appunti:
-t $'\t'In GNU join il più intuitivo -t '\t' , senza il $fallisce, ( coreutils v8.28 e precedenti?); è probabilmente un bug che una soluzione alternativa $dovrebbe essere necessaria. Vedi: unix join separator char .
joinha bisogno di due nomi di file, anche se c'è solo un file su cui si sta lavorando. Usare lo stesso nome due volte joinper eseguire l'azione desiderata.
Per i sistemi con risorse limitate joinoffre un footprint inferiore rispetto ad alcuni degli strumenti utilizzati in altre risposte:
wc -c $(realpath `which cut join sed awk perl`) | head -n -1
43224 /usr/bin/cut
47320 /usr/bin/join
109840 /bin/sed
658072 /usr/bin/gawk
2093624 /usr/bin/perlHo appena lavorato su qualcosa di molto simile, non sono un esperto ma ho pensato di condividere i comandi che ho usato. Avevo un CSV multi colonna di cui avevo bisogno solo di 4 colonne e quindi avevo bisogno di riordinarle.
Il mio file era pipe '|' delimitato ma che può essere sostituito.
LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv
Certo, è davvero grezzo e pronto, ma può essere modificato per adattarlo!
Usando sed
Utilizzare sed con le sottoespressioni nidificate dell'espressione regolare di base per acquisire e riordinare il contenuto della colonna. Questo approccio è più adatto in presenza di un numero limitato di tagli per riordinare le colonne, come in questo caso.
L'idea di base è di circondare parti interessanti del modello di ricerca con \(e \), che possono essere riprodotte nel modello di sostituzione con \#dove #rappresenta la posizione sequenziale della sottoespressione nel modello di ricerca.
Per esempio:
$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"
rendimenti:
bar foo
Il testo esterno a una sottoespressione viene scansionato ma non conservato per la riproduzione nella stringa di sostituzione.
Sebbene la domanda non abbia discusso colonne a larghezza fissa, ne discuteremo qui poiché si tratta di una misura degna di qualsiasi soluzione proposta. Per semplicità supponiamo che il file sia delimitato da spazi sebbene la soluzione possa essere estesa per altri delimitatori.
Spazi collassanti
Per illustrare l'utilizzo più semplice, supponiamo che più spazi possano essere compressi in spazi singoli e che i valori della seconda colonna siano terminati con EOL (e non riempito di spazio).
File:
bash-3.2$ cat f
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 nl
0000040 s t r 2 sp sp sp sp sp sp sp 2 nl s t r
0000060 3 sp sp sp sp sp sp sp 3 nl
0000072
Trasformare:
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000 C o l u m n 2 sp C o l u m n 1 nl
0000020 1 sp s t r 1 nl 2 sp s t r 2 nl 3 sp
0000040 s t r 3 nl
0000045
Preservare le larghezze delle colonne
Ora estendiamo il metodo a un file con colonne a larghezza costante, consentendo allo stesso tempo che le colonne abbiano larghezze diverse.
File:
bash-3.2$ cat f2
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f2
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 sp
0000040 sp sp sp sp sp nl s t r 2 sp sp sp sp sp sp
0000060 sp 2 sp sp sp sp sp sp nl s t r 3 sp sp sp
0000100 sp sp sp sp 3 sp sp sp sp sp sp nl
0000114
Trasformare:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r 2 sp sp sp sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
Infine, sebbene l'esempio della domanda non abbia stringhe di lunghezza diversa, questa espressione sed supporta questo caso.
File:
bash-3.2$ cat f3
Column1 Column2
str1 1
string2 2
str3 3
Trasformare:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1
1 str1
2 string2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r i n g 2 sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
Confronto con altri metodi di riordino delle colonne sotto shell
Sorprendentemente per uno strumento di manipolazione dei file, awk non è adatto per tagliare da un campo alla fine della registrazione. In sed questo può essere realizzato usando espressioni regolari, ad esempio \(xxx.*$\)dove si xxxtrova l'espressione da abbinare alla colonna.
L'uso di incolla e taglia sottotitoli diventa complicato quando si implementano script di shell. Il codice che funziona dalla riga di comando non riesce ad analizzare quando viene portato all'interno di uno script di shell. Almeno questa è stata la mia esperienza (che mi ha spinto a questo approccio).
Espandendo la risposta di @Met, anche usando Perl:
se l'input e l'output sono delimitati da TAB:
perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file
Se l'input e l'output sono delimitati da spazi bianchi:
perl -lane 'print join " ", @F[1, 0]' in_file
Qui,
-edice a Perl di cercare il codice inline, piuttosto che in un file di script separato,
-nlegge l'input 1 riga alla volta,
-lrimuove il separatore del record di input ( \nsu * NIX) dopo aver letto la riga (simile a chomp) e aggiunge output il separatore di record ( \nsu * NIX) per ciascuno print,
-adivide la linea di input su uno spazio bianco in un array @F,
-F'\t'in combinazione con -adivide la linea di input su TAB, anziché uno spazio su un array @F.
@F[1, 0]è l'array composto dal 2o e 1o elemento dell'array @F, in questo ordine. Ricorda che gli array in Perl sono indicizzati zero, mentre i campi in cutsono 1 indicizzati. Quindi i campi in @F[0, 1]sono gli stessi campi incut -f1,2 .
Si noti che tale notazione consente una manipolazione più flessibile dell'input rispetto ad alcune altre risposte postate sopra (che vanno bene per un compito semplice). Per esempio:
# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file
# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file
cutche non supporti questo comando di riordino intuitivo. Comunque, un altro consiglio: puoi usareawkle opzioni-FSe le-OFSopzioni per usare separatori di campi di input e output personalizzati (like-de--output-delimiterforcut).