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
osed
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
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 è chiestols: 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).
RANDOMTEXTTHATWILLNEVERBEINTHEFILE
.