Utilizzo della sostituzione dei parametri su un array Bash

Ho file.txt che devo leggere in un array Bash. Quindi devo rimuovere spazi, virgolette doppie e tutto tranne la prima virgola in ogni voce . Ecco “quanto sono arrivato”:

$ cat file.txt 10,this 2 0 , i s 30,"all" 40,I 50,n,e,e,d,2 60",s e,e" $ cat script.sh #!/bin/bash readarray -t ARRAY<$1 ARRAY=( "${ARRAY[@]// /}" ) ARRAY=( "${ARRAY[@]//\"/}" ) for ELEMENT in "${ARRAY[@]}";do echo "|ELEMENT|$ELEMENT|" done $ ./script.sh file.txt |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,n,e,e,d,2| |ELEMENT|60,se,e| 

Che funziona alla grande tranne che per la situazione della virgola. So che ci sono molti modi per scuoiare questo gatto, ma a causa dello script più grande di cui fa parte, mi piacerebbe molto usare la sostituzione dei parametri per arrivare qui:

|ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

È possibile tramite la sostituzione dei parametri?

Commenti

  • Cè qualche motivo per cui devi mantenere il testo un array e perché non puoi ‘ lasciare ad es. awk o sed elaborano i dati?
  • @Jeff – Il loop sullarray sarà un incubo da implementare nello script più grande su cui ‘ sto lavorando.
  • @JonRed Non ‘ non so cosa stai facendo, quindi ‘ è del tutto possibile che tu non abbia scelta in merito, ma generalmente, quando ti ritrovi a fare acrobazie con stringhe così complesse nella shell, che ‘ è unottima indicazione che dovresti usare un vero linguaggio di programmazione. La shell non è progettata come un linguaggio di programmazione e, sebbene possa essere usata come uno solo, non è ‘ una buona idea per cose più complesse. Ti esorto caldamente a considerare il passaggio a perl o python o qualsiasi altro linguaggio di scripting.
  • @terdon It ‘ è divertente, ho appena finito di dire quasi lesatto stessa cosa al mio collega prima di leggere questo post. Fondamentalmente ho detto che questa è la versione finale di questo script e che qualsiasi ulteriore requisito richiederà una riscrittura in Perl. Quindi sì, sono assolutamente daccordo

Risposta

Rimuoverei ciò che devi rimuovere utilizzando sed prima di caricare nellarray (nota anche i nomi delle variabili in minuscolo, in generale è meglio evitare le variabili in maiuscolo negli script di shell):

#!/bin/bash readarray -t array< <(sed "s/"//g; s/ *//g; s/,/"/; s/,//g; s/"/,/" "$1") for element in "${array[@]}";do echo "|ELEMENT|$element|" done 

Questo produce il seguente output sul tuo file di esempio:

$ foo.sh file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

Se devi davvero usare il parametro sostituzione, prova qualcosa del genere:

#!/bin/bash readarray -t array< "$1" array=( "${array[@]// /}" ) array=( "${array[@]//\"/}" ) array=( "${array[@]/,/\"}" ) array=( "${array[@]//,/}" ) array=( "${array[@]/\"/,}" ) for element in "${array[@]}"; do echo "|ELEMENT|$element|" done 

Commenti

  • @JonRed Ho aggiunto una versione con parametro sostituzione, ma è ‘ complessa, macchinosa e brutta. Fare questo genere di cose nella shell è molto raramente una buona idea.
  • Nota che se ‘ hai rimosso sia gli spazi che le virgolette, questi caratteri diventano disponibili da usare al posto del tuo RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda sì, ho appena letto la tua risposta. Avrei dovuto pensarci! Grazie 🙂
  • Risponde direttamente alla domanda, illustra perché la mia soluzione preferita non è ‘ t ideale e fornisce lalternativa più praticabile. Hai vinto, migliore risposta.

Risposta

Per quanto posso vedere, non è necessario leggilo in un bash array per creare quelloutput:

$ sed "s/[ "]//g; s/,/ /; s/,//g; s/ /,/; s/.*/|ELEMENT|&|/" <file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

Il sed lespressione cancella gli spazi e le virgolette, sostituisce la prima virgola con uno spazio (non ci sono altri spazi nella stringa a questo punto), cancella tutte le altre virgole, ripristina la prima virgola e antepone e aggiunge i dati extra .

In alternativa, con GNU sed:

sed "s/[ "]//g; s/,//2g; s/.*/|ELEMENT|&|/" <file 

(standard sed non supporta la combinazione di 2 e g come flag per s comando).

Commenti

  • con GNU sed, puoi usare 's/,//2g per rimuovere le virgole, a partire dalla seconda
  • E gli ultimi 2 s /// comandi possono essere s/.*/|ELEMENT|&|/ ma potrebbe essere uno sforzo maggiore per sed.
  • @glennjackman Forse, ma sembra piuttosto pulito.
  • Sì, questo fa parte di uno script più ampio. Larray è necessario, non solo per loutput. Da qui il mio interesse per la sostituzione dei parametri. Potrei eseguire il loop dellarray con questo, ma sarà un incubo da implementare. Terndon ha fornito una soluzione senza loop utilizzando sed che io ‘ probabilmente ripiego se la sostituzione dei parametri è impossibile.
  • Se non fossi ‘ t legato alluso di un array, tuttavia, questa sarebbe la soluzione migliore.

Risposta

ELEMENT="50,n,e,e,d,2" IFS=, read -r first rest <<<"$ELEMENT" printf "%s,%s\n" "$first" "${rest//,/}" 
50,need2 

Elimina labitudine di usare nomi di variabili ALLCAPS. Alla fine entrerai in collisione con una variabile di “sistema” cruciale come PATH e interromperai il tuo codice.

Commenti

  • Non sostituzione di parametri. MA, non sapevo che i nomi delle variabili ALLCAPS fossero una cattiva abitudine in Bash. Fai un buon punto, uno che una superficiale ricerca su Google conferma sicuramente. Grazie per aver migliorato il mio stile! 🙂
  • Ho ‘ ho risposto alle domande in cui la persona ha scritto PATH=something; ls $PATH e poi si è chiesto ls: command not found errore.
  • Ci sono quasi un centinaio di variabili incorporate denominate in maiuscolo (fai clic su questa pagina man link ) per vedere …

Risposta

[Questa è essenzialmente una versione più sviluppata versione di glenn jackmann “s answer ]

Creazione di un array associativo dalla chiave e dal valore eliminati, utilizzando la prima virgola come separatore:

declare -A arr while IFS=, read -r k v; do arr["${k//[ \"]}"]="${v//[ ,\"]}"; done < file.txt for k in "${!arr[@]}"; do printf "|ELEMENT|%s,%s|\n" "$k" "${arr[$k]}" done |ELEMENT|20,is| |ELEMENT|10,this| |ELEMENT|50,need2| |ELEMENT|40,I| |ELEMENT|60,see| |ELEMENT|30,all| 

Risposta

Puoi scorrere larray e utilizzare una variabile intermedia:

for((i=0; i < "${#ARRAY[@]}"; i++)) do rest="${ARRAY[i]#*,}" ARRAY[i]="${ARRAY[i]%%,*}","${rest//,/}" done 

Questo assegna a rest la parte dopo la prima virgola; quindi concateniamo tre pezzi nelloriginale variabile:

  • la parte prima della prima virgola
  • una virgola
  • la sostituzione in rest di ogni virgola senza nulla

Commenti

  • Questo è stato il mio primo pensiero ed è abbastanza semplice per lesempio, ma fa parte di uno script più grande in cui larray è enorme e ‘ è già in loop e sarebbe una cosa intera. Questo funzionerebbe sicuramente, ma sarebbe molto complicato da implementare nel progetto più ampio su cui ‘ sto lavorando.
  • Abbastanza giusto; Ho solo provato a rispondere entro i limiti (solo espansione dei parametri).

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *