Come usare un comando shell per mostrare solo la prima colonna e l'ultima colonna in un file di testo?


30

Ho bisogno di aiuto per capire come usare il comando sed per mostrare solo la prima colonna e l'ultima colonna in un file di testo. Ecco quello che ho finora per la colonna 1:

cat logfile | sed 's/\|/ /'|awk '{print $1}'

Il mio debole tentativo di mostrare anche l'ultima colonna era:

cat logfile | sed 's/\|/ /'|awk '{print $1}{print $8}'

Tuttavia, questa prende la prima colonna e l'ultima colonna e le unisce in un unico elenco. C'è un modo per stampare chiaramente la prima colonna e le ultime colonne con i comandi sed e awk?

Input di esempio:

foo|dog|cat|mouse|lion|ox|tiger|bar

5
Fornisci alcuni esempi di input.
Jasonwryan,

Risposte:


51

Quasi lì. Metti solo i riferimenti di colonna uno accanto all'altro.

cat logfile | sed 's/|/ /' | awk '{print $1, $8}'

Si noti inoltre che non è necessario catqui.

sed 's/|/ /' logfile | awk '{print $1, $8}'

Si noti inoltre che si può dire awkche i separatori di colonna sono |, anziché spazi vuoti, quindi non è necessario sedneanche.

awk -F '|' '{print $1, $8}' logfile

Come suggerito da Caleb , se si desidera una soluzione che emetta ancora l'ultimo campo, anche se non ce ne sono esattamente otto, è possibile utilizzare $NF.

awk -F '|' '{print $1, $NF}' logfile

Inoltre, se si desidera che l'output conservi i |separatori, anziché utilizzare uno spazio, è possibile specificare i separatori del campo di output. Sfortunatamente, è un po 'più goffo che usare semplicemente la -Fbandiera, ma qui ci sono tre approcci.

  • È possibile assegnare i separatori di campo di input e output in awksé, nel blocco BEGIN.

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
  • È possibile assegnare queste variabili quando si chiama awkdalla riga di comando, tramite il -vflag.

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
  • o semplicemente:

    awk -F '|' '{print $1 "|" $8}' logfile

4
Un buon lavoro che analizza come questo problema può essere semplificato. È possibile aggiungere una nota su come utilizzare |come separatore di output anziché lo spazio predefinito per la concatenazione di stringhe. Inoltre potresti spiegare di usare $NFinvece di hard coding $8per ottenere l'ultima colonna.
Caleb,

12

Sostituisci dal primo all'ultimo |con un |(o spazio se preferisci):

sed 's/|.*|/|/'

Nota che sebbene non ci sia sedimplementazione in cui |sia speciale (purché le espressioni regolari estese non siano abilitate tramite -Eo -rin alcune implementazioni), \|è di per sé speciale in alcuni come GNU sed. Così si dovrebbe non sfuggire |se si intende in modo che corrisponda al |carattere.

Se si sostituisce con spazio e se l'input può già contenere righe con una sola |, allora è necessario trattarlo in modo speciale in quanto |.*|non corrisponderà a quelli. Potrebbe essere:

sed 's/|\(.*|\)\{0,1\}/ /'

(ovvero rendere .*|opzionale la parte) Oppure:

sed 's/|.*|/ /;s/|/ /'

o:

sed 's/\([^|]*\).*|/\1 /'

Se vuoi il primo e l'ottavo campo indipendentemente dal numero di campi nell'input, allora è solo:

cut -d'|' -f1,8


(tutti quelli funzionerebbero con qualsiasi utilità conforme a POSIX assumendo che l'input formi un testo valido (in particolare, sedquelli generalmente non funzioneranno se l'input ha byte o sequenze di byte che non formano caratteri validi nella locale corrente come ad esempio printf 'unix|St\351phane|Chazelas\n' | sed 's/|.*|/|/'in una locale UTF-8)).


11

Stai usando awkcomunque:

awk '{ print $1, $NF }' file

2
Non avresti bisogno di specificare il separatore del campo di input (poiché in questo caso sembra |piuttosto quello spazio) con -F\|o simile? E se volesse usare lo stesso delimitatore per l'output?
Caleb,

@Caleb Probabilmente: stavo aspettando che l'OP confermasse esattamente come appariva l'input, piuttosto che cercare di indovinare sulla base degli esempi non funzionanti ...
Jasonwryan,

1
Si noti che ciò presuppone che l'input contenga almeno 2 campi.
Stéphane Chazelas,

@ StéphaneChazelas OP ha chiaramente indicato nel codice che ha otto campi, sempre.
michaelb958 - Ripristina Monica il

3
@ michaelb958 Penso che "chiaramente" stia sopravvalutando il caso, solo un po ':)
Jasonwryan,

4

Se ti trovi in ​​imbarazzo e sed-less, puoi ottenere la stessa cosa con coreutils:

paste <(           cut -d'|' -f1  file) \ 
      <(rev file | cut -d'|' -f1 | rev)

cutè più pulito e più compatto di awk / sed quando sei solo interessato alla prima colonna o se i delimitatori sono fissi (cioè non un numero variabile di spazi).
Sridhar Sarnobat,

2

Sembra che tu stia cercando di ottenere il primo e l'ultimo campo di testo che sono delimitati da |.

Presumo che il tuo file di registro contenga il testo come di seguito,

foo|dog|cat|mouse|lion|ox|tiger|bar
bar|dog|cat|mouse|lion|ox|tiger|foo

E vuoi l'output come,

foo bar
bar foo

Se sì, ecco che arriva il comando per il tuo

Attraverso GNU sed,

sed -r 's~^([^|]*).*\|(.*)$~\1 \2~' file

Esempio:

$ echo 'foo|dog|cat|mouse|lion|ox|tiger|bar' | sed -r 's~^([^|]*).*\|(.*)$~\1 \2~'
foo bar

Le colonne non sono delimitate da una pipe | ma sono in colonne, mi interessa usare sed ma non usare il comando awk come hai fatto nel tuo comando: sed -r 's ~ ^ ([^ |] *). * \ | (. *) $ ~ \ 1 \ 2 ~ 'file
user70573

"Le colonne non sono delimitate da una pipe | ma sono in colonne", vuoi dire che le colonne sono separate da spazi?
Avinash Raj,

Un input di esempio e un output sarebbero migliori.
Avinash Raj,

1

Probabilmente dovresti farlo con sed- lo farei comunque - ma, solo perché nessuno ha ancora scritto questo:

while IFS=\| read col1 cols
do  printf %10s%-s\\n "$col1 |" " ${cols##*|}"
done <<\INPUT
foo|dog|cat|mouse|lion|ox|tiger|bar
INPUT

PRODUZIONE

     foo | bar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.