Trasformare una stringa multilinea in una singola virgola separata da virgole


95

Diciamo che ho la seguente stringa:

something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)

Come lo trasformo in semplicemente

+12.0,+15.5,+9.0,+13.5

in bash?


Facciamo un passo indietro per un momento e consideriamo questo thread un'evidente accusa di bash come linguaggio di programmazione. Si consideri Scala's listOfStuff mkString ", ", o Haskell'sintercalate ", " listOfString
FP Freely

Risposte:


92

Puoi usare awke sed:

awk -vORS=, '{ print $2 }' file.txt | sed 's/,$/\n/'

O se vuoi usare una pipa:

echo "data" | awk -vORS=, '{ print $2 }' | sed 's/,$/\n/'

Per scomporlo:

  • awk è ottimo per gestire i dati suddivisi in campi
  • -vORS=,imposta il "separatore di record di output" su ,, che è quello che volevi
  • { print $2 }dice awkdi stampare il secondo campo per ogni record (riga)
  • file.txt è il nome del tuo file
  • sedelimina solo il trailing ,e lo trasforma in una nuova riga (se non vuoi una nuova riga, puoi farlo s/,$//)

1
awk: opzione -v non valida :(
Marsellus Wallace

6
Aggiungi uno spazio tra -v e ORS =, (per me, su osx)
Graham P Heath

Come eseguire lo stesso comando per separare le pipe? awk -v ORS=| '{ print $1 }' DCMC.rtf | sed 's/,$/\n/'ricevo un errore
Yogesh il

2
stranamente, quando provo a farlo, l'output è vuoto.
eternaltyro

1
Penso che per la versione in pipe dovrebbe essere {print $1}altrimenti ottengo solo virgole nell'output
Przemysław Czechowski,

162

Pulito e semplice:

awk '{print $2}' file.txt | paste -s -d, -

3
Questa è la migliore risposta qui, e ovviamente il modo corretto per farlo
forresthopkinsa

Come cito tutti i valori con virgolette singole / doppie?
Hussain

1
@Hussaincat thing | awk -F',' '{ print "'\''" $7 "'\' '" }' | paste -s -d ','
starbeamrainbowlabs

Come si usa ,'come delimitatore?
Kasun Siyambalapitiya

Ricordarsi di gestire le nuove righe di Windows (ad esempio utilizzando dos2unix) se ci sono CRLF nella stringa.
Bowi


10
$ awk -v ORS=, '{print $2}' data.txt | sed 's/,$//'
+12.0,+15.5,+9.0,+13.5

$ cat data.txt | tr -s ' ' | cut -d ' ' -f 2 | tr '\n' ',' | sed 's/,$//'
+12.0,+15.5,+9.0,+13.5

applausi, e se l'input per awk fosse tramite l'input standard (metti solo function | awk...nel tuo esempio?
Alex Coplan

10

awk one liner

$ awk '{printf (NR>1?",":"") $2}' file

+12.0,+15.5,+9.0,+13.5

8

Anche questo dovrebbe funzionare

awk '{print $2}' file | sed ':a;{N;s/\n/,/};ba'

8

Questo potrebbe funzionare per te:

cut -d' ' -f5 file | paste -d',' -s
+12.0,+15.5,+9.0,+13.5

o

sed '/^.*\(+[^ ]*\).*/{s//\1/;H};${x;s/\n/,/g;s/.//p};d' file
+12.0,+15.5,+9.0,+13.5

o

sed 's/\S\+\s\+//;s/\s.*//;H;$!d;x;s/.//;s/\n/,/g' file

Per ogni riga del file; tagliare il primo campo e gli spazi seguenti, tagliare il resto della riga che segue il secondo campo e aggiungere allo spazio di attesa. Elimina tutte le righe tranne l'ultima dove passiamo allo spazio di attesa e dopo aver eliminato la nuova riga introdotta all'inizio, converti tutte le nuove righe in ,"s".

NB Potrebbe essere scritto:

sed 's/\S\+\s\+//;s/\s.*//;1h;1!H;$!d;x;s/\n/,/g' file

4

Puoi usare grep:

grep -o "+\S\+" in.txt | tr '\n' ','

che trova la stringa che inizia con +, seguita da qualsiasi stringa \S\+, quindi converte i caratteri della nuova riga in virgole. Questo dovrebbe essere abbastanza veloce per file di grandi dimensioni.


4

Prova questo semplice codice:

awk '{printf("%s,",$2)}' File1

3

prova questo:

sedSelectNumbers='s".* \(+[0-9]*[.][0-9]*\) .*"\1,"'
sedClearLastComma='s"\(.*\),$"\1"'
cat file.txt |sed "$sedSelectNumbers" |tr -d "\n" |sed "$sedClearLastComma"

la cosa buona è la parte facile dell'eliminazione dei caratteri "\ n" di nuova riga!

EDIT: un altro ottimo modo per unire le linee in una singola linea con sed è questo: |sed ':a;N;$!ba;s/\n/ /g'ottenuto da qui .


Quella MODIFICA è fantastica - +1!
JoeG

2

Una soluzione scritta in puro Bash:

#!/bin/bash

sometext="something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)"

a=()
while read -r a1 a2 a3; do
    # we can add some code here to check valid values or modify them
    a+=("${a2}")
done <<< "${sometext}"
# between parenthesis to modify IFS for the current statement only
(IFS=',' ; printf '%s: %s\n' "Result" "${a[*]}")

Risultato: + 12,0, + 15,5, + 9,0, + 13,5


2

Non ho visto questa semplice soluzione con awk

awk 'b{b=b","}{b=b$2}END{print b}' infile

0

Con perl:

fg@erwin ~ $ perl -ne 'push @l, (split(/\s+/))[1]; END { print join(",", @l) . "\n" }' <<EOF
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)
EOF

+12.0,+15.5,+9.0,+13.5

0

Puoi anche farlo con due chiamate sed:

$ cat file.txt 
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)
$ sed 's/^[^:]*: *\([+0-9.]\+\) .*/\1/' file.txt | sed -e :a -e '$!N; s/\n/,/; ta'
+12.0,+15.5,+9.0,+13.5

La prima chiamata sed rimuove i dati non interessanti e la seconda unisce tutte le linee.


0

Puoi anche stampare in questo modo:

Semplicemente awk: usando printf

bash-3.2$ cat sample.log
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)

bash-3.2$ awk ' { if($2 != "") { if(NR==1) { printf $2 } else { printf "," $2 } } }' sample.log
+12.0,+15.5,+9.0,+13.5

0

Un'altra soluzione Perl, simile a awk di Dan Fego:

perl -ane 'print "$F[1],"' file.txt | sed 's/,$/\n/'

-a dice a perl di dividere la riga di input nell'array @F, che è indicizzato a partire da 0.


0

Ebbene, la parte più difficile probabilmente è selezionare la seconda "colonna" poiché non saprei come trattare più spazi come uno solo. Per il resto è facile. Usa le sostituzioni bash.

# cat bla.txt
something1:    +12.0   (some unnecessary trailing data (this must go))
something2:    +15.5   (some more unnecessary trailing data)
something4:    +9.0   (some other unnecessary data)
something1:    +13.5  (blah blah blah)

# cat bla.sh
OLDIFS=$IFS
IFS=$'\n'
for i in $(cat bla.txt); do
  i=$(echo "$i" | awk '{print $2}')
  u="${u:+$u, }$i"
done
IFS=$OLDIFS
echo "$u"

# bash ./bla.sh
+12.0, +15.5, +9.0, +13.5
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.