Come dividere una stringa delimitata in un array in awk?


169

Come dividere la stringa quando contiene simboli pipe |. Voglio dividerli per essere in array.

Provai

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

Che funziona benissimo. Se la mia stringa è come "12|23|11"allora come posso dividerli in un array?


3
Si noti che l'output sta concatenando gli elementi dell'array, senza separatore. Se invece volevi che fossero separati OFS, inserisci le virgole tra loro, facendole printvedere come argomenti separati.
dubiousjim,

Oppure puoi usare sed:echo "12:23:11" | sed "s/.*://"
fangoso

@slushy: il tuo comando non è affatto ciò di cui ha bisogno il richiedente. il tuo comando ( echo "12:23:11" | sed "s/.*://") elimina tutto fino a (e includendo) l'ultimo ":", mantenendo solo "11" ... funziona per ottenere l'ultimo numero, ma dovrebbe essere modificato (in un modo difficile da leggere) per ottenere il secondo numero, ecc. awk (e la divisione di awk) è molto più elegante e leggibile.
Olivier Dulac il

se devi dividere un singolo personaggio puoi usarecut
ccpizza l'

Risposte:


274

Hai provato:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'

2
@Mohamed Saligh, se utilizzi Solaris, devi usare / usr / xpg4 / bin / awk , data la lunghezza della stringa.
Dimitre Radoulov,

5
'non funziona per me'. specialmente con due punti tra i valori echo e split impostato per dividere su '|' ??? Errore di battitura? Buona fortuna a tutti.
shellter

1
Meglio con qualche spiegazione della sintassi.
Alston,

2
Questo non funzionerà in GNU awk, perché il terzo argomento splitè un'espressione regolare ed |è un simbolo speciale, che deve essere evitato. Usasplit($0, a, "\|")
WhiteWind

1
@WhiteWind: un altro modo per "garantire" che |sia visto come un carattere e non un simbolo speciale è quello di metterlo tra []: cioè, split($0, a, "[|]") # Mi piace di più di '\ |', in alcuni casi, specialmente come una variante di regexp ( perl vs grep vs .. altri?) può avere "|" interpretato letteralmente e "\ |" visto come regex separator, invece del contrario ... ymmv
Olivier Dulac

119

Per dividere una stringa in un array awkutilizziamo la funzione split():

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

Se non viene fornito alcun separatore, utilizza il FSvalore predefinito per lo spazio:

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

Possiamo dare un separatore, ad esempio ::

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

Ciò equivale a impostarlo tramite FS:

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

In gawk puoi anche fornire il separatore come regexp:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

E anche vedere cosa era il delimitatore in ogni passaggio usando il suo quarto parametro:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

Citiamo la pagina man di GNU awk :

split (stringa, array [, fieldsep [, seps]])

Dividi la stringa in pezzi separati da FieldSep e archivia i pezzi nell'array e le stringhe del separatore nell'array di seps . Il primo pezzo viene archiviato array[1], il secondo pezzo array[2]e così via. Il valore di stringa del terzo argomento, FieldPep , è una regexp che descrive dove dividere la stringa (così come FS può essere una regexp che descrive dove dividere i record di input). Se FieldP viene omesso, viene utilizzato il valore di FS . split()restituisce il numero di elementi creati. seps è gawkun'estensione, con seps[i]la stringa di separazione traarray[i]e array[i+1]. Se fieldsep è un singolo spazio, allora entra in gioco seps[0]qualsiasi spazio bianco iniziale e in qualsiasi spazio bianco finale seps[n], dove n è il valore di ritorno di split()(cioè il numero di elementi nell'array).


basta menzionare che stai usando gnu awk, non un normale awk (che non memorizza i separatori in seps [] e ha altre limitazioni)
Olivier Dulac il

17

Sii più specifico! Cosa intendi con "non funziona"? Pubblica l'output esatto (o il messaggio di errore), il tuo sistema operativo e la versione awk:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

Oppure, usando split:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

Modifica: su Solaris dovrai usare POSIX awk ( / usr / xpg4 / bin / awk ) per elaborare correttamente 4000 campi.


for(i = 0o for(i = 1?
PiotrNycz,

i = 0, perché uso ++ i dopo (non i ++).
Dimitre Radoulov,

3
Ok - Non me ne sono accorto. Credo fermamente che sarebbe più leggibile for (i = 1; i <= n; ++i)...
PiotrNycz,

5

Non mi piace la echo "..." | awk ...soluzione in quanto chiama chiamate di sistema forke non necessarie exec.

Preferisco la soluzione di un Dimitre con una piccola svolta

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

O una versione un po 'più corta:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

In questo caso il record di output ha messo insieme che è una condizione vera, quindi viene stampato.

In questo caso specifico il stdinreindirizzamento può essere risparmiato impostando un variabile interna:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

ero solito un bel po ', ma dentro questo potrebbe essere gestito dalla manipolazione di stringhe interne. Nel primo caso la stringa originale viene divisa per terminatore interno. Nel secondo caso si presume che la stringa contenga sempre coppie di cifre separate da un separatore di un carattere.

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

Il risultato è in tutti i casi

112312

Penso che il risultato finale avrebbe dovuto essere i riferimenti alle variabili dell'array awk, indipendentemente dall'esempio di output di stampa fornito. Ma hai perso un caso bash davvero semplice per fornire il tuo risultato finale. T = '12: 23: 11 '; echo $ {T //:}
Daniel Liston

@DanielListon Hai ragione! Grazie! Non sapevo che il trailing / potesse essere lasciato in questa bashespressione ...
TrueY

4

In realtà awkha una funzione chiamata link "Input Field Separator Variable" . Ecco come usarlo. Non è in realtà un array, ma utilizza le variabili $ interne. Per dividere una semplice stringa è più facile.

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'

3
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

dovrebbe funzionare.



1

Scherzare? :)

Che ne dite di echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

Questa è la mia uscita:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

quindi immagino che funzioni dopo tutto ..


è a causa della lunghezza della stringa? da allora, la mia lunghezza della corda è 4000. qualsiasi idea
Mohamed Saligh

1

So che questa è una specie di vecchia domanda, ma ho pensato che forse a qualcuno piace il mio trucco. Soprattutto perché questa soluzione non si limita a un numero specifico di articoli.

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

L'output sarà:

Item: 12
Item: 23
Item: 11
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.