Ved å bruke parameterutskifting på en Bash-array

har jeg file.txt som jeg trenger å lese inn i en Bash-array. Da må jeg fjerne mellomrom, doble anførselstegn og alt bortsett fra det første kommaet i hver oppføring . Her er hvor langt jeg har kommet:

$ 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| 

Som fungerer bra bortsett fra komma-situasjonen. Jeg er klar over at det er flere måter å flå denne katten på, men på grunn av det større skriptet dette er en del av, vil jeg virkelig bruke parameterutskiftning for å komme hit:

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

Er dette mulig via parameterutskiftning?

Kommentarer

  • Er det noen grunn til at du trenger å holde teksten inne en matrise, og hvorfor du ikke kan ‘ t la f.eks. awk eller sed gjør behandlingen av dataene?
  • @Jeff – Looping over array vil være en mareritt å implementere i det større skriptet jeg ‘ jeg jobber med.
  • @JonRed Jeg vet ikke ‘ vet ikke hva du gjør, så det ‘ er fullt mulig at du kanskje ikke har et valg i saken, men generelt sett når du finner deg selv i å gjøre så komplekse strengakrobatikk i skallet, at ‘ er en veldig god indikasjon på at du skal bruke et faktisk programmeringsspråk. Skallet er ikke designet som et programmeringsspråk, og selv om det kan brukes som et, er det ikke ‘ ikke en god idé for mer komplekse ting. Jeg oppfordrer deg sterkt til å vurdere å bytte til perl eller python eller et hvilket som helst annet skriptspråk.
  • @terdon Det er ‘ morsomt, jeg har akkurat fått sagt det nesten det samme til min kollega før jeg leste dette innlegget. Jeg sa i utgangspunktet at dette er den endelige versjonen av dette skriptet, og at eventuelle ytterligere krav vil kreve omskriving i Perl. Så ja, jeg er absolutt enig

Svar

Jeg vil fjerne det du trenger å fjerne ved å bruke sed før lastes inn i matrisen (merk også små versjonsnavn, generelt er det best å unngå store variabler i skallskript):

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

Dette gir følgende utdata på eksempelfilen din:

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

Hvis du virkelig må bruke parameter erstatning, prøv noe sånt:

#!/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 

Kommentarer

  • @JonRed Jeg la til en versjon med parameter erstatning men den ‘ er kompleks, tungvint og stygg. Å gjøre denne typen ting i skallet er svært sjelden en god ide.
  • Merk at hvis du ‘ har fjernet begge mellomrom og doble anførselstegn, blir disse tegnene tilgjengelige å bruke i stedet for RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda ja, jeg har nettopp lest svaret ditt. Bør ha tenkt på det! Takk 🙂
  • Svarer direkte på spørsmålet, illustrerer hvorfor min foretrukne løsning ikke er ‘ t ideell, og gir det mest levedyktige alternativet. Du vinner, beste svaret.

Svar

Så vidt jeg kan se, er det ikke behov for les den inn i et bash -array for å lage den utgangen:

$ 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| 

sed uttrykk sletter mellomrom og doble anførselstegn, erstatter det første kommaet med et mellomrom (det er ingen andre mellomrom i strengen på dette punktet), sletter alle andre kommaer, gjenoppretter det første kommaet, og legger til og legger til ekstra data .

Alternativt, med GNU sed:

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

(standard sed støtter ikke kombinasjonen av 2 og g som flagg til s kommando).

Kommentarer

  • med GNU sed, kan du bruke 's/,//2g for å fjerne komma, starter med det andre
  • Og de siste 2 s /// kommandoene kan være s/.*/|ELEMENT|&|/ men det kan være mer innsats for sed.
  • @glennjackman Muligens, men det ser ganske pent ut.
  • Ja, dette er en del av et større manus. Matrisen er nødvendig, ikke bare for utdataene. Derav min interesse for parameterutskifting. Jeg kunne løpe over matrisen med dette, men det vil være et mareritt å implementere. Terndon ga en løyfefri løsning ved å bruke sed som jeg ‘ sannsynligvis vil falle tilbake på hvis parameterutskiftning ikke er tillatt.
  • Hvis jeg ikke var ‘ t knyttet til å bruke en matrise, men dette ville være den beste løsningen.

Svar

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

Gå ut av vanen med å bruke ALLCAPS-variabelnavn. Du vil til slutt kollidere med en viktig «system» -variabel som PATH og bryte koden din.

Kommentarer

  • Ikke parametererstatning. MEN jeg var ikke klar over at ALLCAPS-variabelnavn var en dårlig vane i Bash. Du gjør et godt poeng, en som en kortvarig googling definitivt bekrefter. Takk for at du forbedret stilen min! 🙂
  • Jeg ‘ har svart på spørsmål der personen skrev PATH=something; ls $PATH og så lurte på ls: command not found feil.
  • Det er nesten hundre innebygde variabler som er navngitt i alle bokstaver (klikk gjennom denne man-siden lenke ) for å se …

Svar

[Dette er egentlig et mer fullt utviklet versjon av glenn jackmanns svar ]

Bygger en assosiativ matrise fra den strippede nøkkelen og verdien, ved å bruke det første kommaet som skilletegn:

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| 

Svar

Du kan sløyfe over matrisen og bruke en mellomvariabel:

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

Dette tildeler rest delen etter det første kommaet; deretter sammenkobles tre stykker tilbake til originalen variabel:

  • delen før det første kommaet
  • et komma
  • erstatningen i rest av hvert komma med ingenting

Kommentarer

  • Dette var min første tanke og er enkel nok for eksemplet, men dette er en del av større skript der matrisen er massiv og der ‘ allerede sløyfer og det ville være en hel ting. Dette ville definitivt fungere, men ville være veldig tungvint å implementere i det større prosjektet jeg ‘ jobber med.
  • Greit nok; Jeg prøvde bare å svare innenfor begrensningene (bare parameterutvidelse).

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *