Ved hjælp af parameterudskiftning på et Bash-array

har jeg file.txt, som jeg skal læse i et Bash-array. Derefter skal jeg fjerne mellemrum, dobbelt anførselstegn og alt undtagen det første komma i hver post . Her er hvor langt jeg er 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| 

Hvilket fungerer godt bortset fra komma-situationen. Jeg er opmærksom på, at der er flere måder at flå denne kat på, men på grund af det større script, dette er en del af, vil jeg meget gerne bruge parameterudskiftning for at komme hertil:

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

Er dette muligt via parameterudskiftning?

Kommentarer

  • Er der nogen grund til at du har brug for at holde teksten inde et array, og hvorfor du ikke kan ‘ t lade f.eks. awk eller sed gør behandlingen af dataene?
  • @Jeff – Looping over arrayet vil være en mareridt at implementere i det større script jeg ‘ arbejder på.
  • @JonRed Jeg ved ikke ‘ ikke hvad du gør, så det er ‘, at du muligvis ikke har et valg i sagen, men generelt, når du finder dig selv i at gøre så komplekse strengakrobatik i skallen, at ‘ er en meget god indikation af, at du skal bruge et faktisk programmeringssprog. Skallen er ikke designet som et programmeringssprog, og selvom den kan bruges som en, er den virkelig ikke ‘ t en god idé til mere komplekse ting. Jeg opfordrer dig kraftigt til at overveje at skifte til perl eller python eller ethvert andet scriptingsprog.
  • @terdon Det ‘ er sjovt, jeg er lige færdig med at sige næsten det nøjagtige samme ting til min kollega, før jeg læste dette indlæg. Jeg sagde dybest set, at dette er den endelige version af dette script, og at eventuelle yderligere krav vil kræve omskrivning i Perl. Så ja, jeg er bestemt enig

Svar

Jeg fjerner det, du skal fjerne, ved hjælp af sed inden indlæsning i arrayet (bemærk også små variabelnavne, generelt er det bedst at undgå kapitaliserede variabler i shell-scripts):

#!/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 producerer følgende output på din eksempelfil:

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

Hvis du virkelig skal bruge parameter erstatning, prøv noget som dette:

#!/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 tilføjede en version med parameter erstatning, men det er ‘ s komplekst, besværligt og grimt. At gøre denne slags ting i skallen er meget sjældent en god idé.
  • Bemærk, at hvis du ‘ har fjernet begge mellemrum og dobbelte citater, bliver disse tegn tilgængelige at bruge i stedet for din RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda ja, jeg har lige læst dit svar. Burde have tænkt på det! Tak 🙂
  • Svarer direkte på spørgsmålet, illustrerer hvorfor min foretrukne løsning ikke er ‘ t ideel og giver det mest levedygtige alternativ. Du vinder, bedste svar.

Svar

Så vidt jeg kan se, er der ingen grund til læs det ind i et bash array for at skabe det output:

$ 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 udtryk sletter mellemrum og dobbelt anførselstegn, erstatter det første komma med et mellemrum (der er ingen andre mellemrum i strengen på dette tidspunkt), sletter alle andre kommaer, gendanner det første komma, og forudbetaler og tilføjer de ekstra data .

Alternativt med GNU sed:

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

(standard sed understøtter ikke kombinationen af 2 og g som flag til s kommando).

Kommentarer

  • med GNU sed, du kan bruge 's/,//2g for at fjerne kommaer, startende med 2.
  • Og de sidste 2 s /// kommandoer kan være s/.*/|ELEMENT|&|/ men det kan være en større indsats for sed.
  • @glennjackman Muligvis, men det ser ret pænt ud.
  • Ja, dette er en del af et større script. Arrayet er nødvendigt, ikke kun for output. Derfor er min interesse i parameterudskiftning. Jeg kunne løbe over arrayet med dette, men det vil være et mareridt at implementere. Terndon leverede en løkkefri løsning ved hjælp af sed, som jeg ‘ sandsynligvis falder tilbage på, hvis parameterudskiftning ikke er tilladt.
  • Hvis jeg ikke var ‘ ikke bundet til at bruge en matrix, men dette ville være den bedste løsning.

Svar

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

Bliv vant til at bruge ALLCAPS-variabelnavne. Du kolliderer til sidst med en vigtig “system” -variabel som PATH og bryder din kode.

Kommentarer

  • Ikke parametererstatning. MEN jeg var ikke klar over, at ALLCAPS-variabelnavne var en dårlig vane i Bash. Du gør et godt punkt, en som en kortvarig googling bestemt bekræfter. Tak fordi du forbedrede min stil! 🙂
  • Jeg ‘ har besvaret spørgsmål, hvor personen skrev PATH=something; ls $PATH og derefter undrede sig over ls: command not found fejl.
  • Der er næsten hundrede indbyggede variabler, der er navngivet i alle bogstaver (klik gennem denne mandside link ) for at se …

Svar

[Dette er i det væsentlige en mere fuldt udviklet version af glenn jackmanns svar ]

Opbygning af et associerende array fra den strippede nøgle og værdi ved hjælp af det første komma som separator:

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 løbe over arrayet og bruge en mellemvariabel:

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

Dette tildeler rest delen efter det første komma; vi sammenkæder derefter tre stykker tilbage i originalen variabel:

  • delen før det første komma
  • et komma
  • udskiftning i rest af ethvert komma med intet

Kommentarer

  • Dette var min første tanke og er enkel nok til eksemplet, men dette er en del af større script, hvor arrayet er massivt, og der ‘ allerede sløjfer, og det ville være en hel ting. Dette ville helt sikkert fungere, men ville være meget besværligt at implementere i det større projekt, jeg ‘ arbejder på.
  • Fair nok; Jeg prøvede bare at svare inden for begrænsningerne (kun parameterudvidelse).

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *