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
ellersed
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
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).
RANDOMTEXTTHATWILLNEVERBEINTHEFILE
.