Folosind substituirea parametrilor pe un tablou Bash

Am file.txt pe care trebuie să îl citesc într-un tablou Bash. Apoi, trebuie să elimin spații, ghilimele duble și toate, cu excepția primei virgule din fiecare intrare . Iată cât de departe am ajuns:

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

Care funcționează excelent, cu excepția situației virgulei. Sunt conștient de faptul că există mai multe moduri de a jupui această pisică, dar datorită scriptului mai mare din care face parte, aș vrea să folosesc înlocuirea parametrilor pentru a ajunge aici:

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

Este posibil prin substituirea parametrilor?

Comentarii

  • Există vreun motiv pentru care trebuie să păstrați textul în o matrice și de ce nu poți ‘ să lași de ex. awk sau sed procesează datele?
  • @Jeff – Buclarea peste matrice va fi o coșmar de implementat în scriptul mai mare pe care ‘ lucrez.
  • @JonRed Nu ‘ nu știu ce faceți, deci este ‘ complet posibil să nu aveți de ales în această materie, dar, în general, atunci când vă aflați în acrobații complexe de șiruri în shell, ca ‘ este o indicație foarte bună că ar trebui să utilizați un limbaj de programare propriu-zis. Shell-ul nu este conceput ca limbaj de programare și, deși poate fi folosit ca unul, nu este într-adevăr ‘ o idee bună pentru lucruri mai complexe. Vă îndemn insistent să luați în considerare trecerea la perl sau python sau la orice alt limbaj de scriptare.
  • @terdon Este ‘ amuzant, tocmai am terminat de spus aproape exact același lucru și colegului meu înainte să citesc această postare. Practic am spus că aceasta este versiunea finală a acestui script și că orice alte cerințe vor necesita rescrierea în Perl. Deci da, sunt de acord cu siguranță

Răspuns

Aș elimina ceea ce trebuie să eliminați folosind înainte încărcarea în matrice (rețineți și numele minusculelor variabilelor, în general, cel mai bine este să evitați variabilele cu majuscule în scripturile 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 

Aceasta produce următoarea ieșire din fișierul dvs. de exemplu:

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

Dacă într-adevăr trebuie să utilizați parametrul substituție, încercați ceva de genul acesta:

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

Comentarii

  • @JonRed Am adăugat o versiune cu parametru substituție, dar este ‘ complexă, greoaie și urâtă. Efectuarea acestui gen de lucruri în shell este foarte rar o idee bună.
  • Rețineți că, dacă ați eliminat ambele spații și ghilimele, aceste caractere devin disponibile de folosit în loc de RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda da, tocmai am citit răspunsul tău. Ar fi trebuit să mă gândesc la asta! Mulțumesc 🙂
  • Răspunde direct la întrebare, ilustrează de ce soluția mea preferată nu este ‘ t ideală și oferă cea mai viabilă alternativă. Câștigă, cel mai bun răspuns.

Răspuns

Din câte văd, nu este nevoie să citiți-l într-o matrice bash pentru a crea acea ieșire:

$ 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 expresia șterge spații și ghilimele duble, înlocuiește prima virgulă cu un spațiu (nu există alte spații în șir în acest moment), șterge toate celelalte virgule, restabilește prima virgulă, și prependează și adaugă datele suplimentare .

Alternativ, cu GNU sed:

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

(standard sed nu acceptă combinația 2 și g ca steaguri către s comanda).

Comentarii

  • cu GNU sed, puteți utiliza 's/,//2g pentru a elimina virgulele, începând cu a doua
  • Și ultimele 2 s /// comenzi pot fi s/.*/|ELEMENT|&|/ dar ar putea fi un efort mai mare pentru sed.
  • @glennjackman Posibil, dar pare destul de îngrijit.
  • Da, aceasta face parte dintr-un script mai mare. Matricea este necesară, nu doar pentru ieșire. De aici și interesul meu pentru substituirea parametrilor. Aș putea face o buclă peste matrice cu asta, dar acesta va fi un coșmar de implementat. Terndon a oferit o soluție fără buclă folosind sed, pe care ‘ probabil că o să recurg dacă substituirea parametrilor este interzisă.
  • Dacă nu aș fi ‘ legat de utilizarea unei matrice, totuși aceasta ar fi cea mai bună soluție.

Răspuns

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

Ieșiți din obișnuința de a folosi numele variabilelor ALLCAPS. În cele din urmă vă veți ciocni cu o variabilă crucială de „sistem”, cum ar fi PATH, și vă va rupe codul.

Comentarii

  • Nu substituirea parametrilor. DAR, nu știam că numele variabilelor ALLCAPS erau un obicei prost în Bash. Faceți un punct bun, unul pe care un googling rapid îl confirmă cu siguranță. Vă mulțumesc că mi-ați îmbunătățit stilul! 🙂
  • Am ‘ răspuns la întrebări în care persoana respectivă a scris PATH=something; ls $PATH și apoi m-am întrebat despre ls: command not found.
  • Există aproape o sută de variabile încorporate care sunt denumite în toate majusculele (faceți clic pe această pagină manuală link ) pentru a vedea …

Răspuns

[Acesta este, în esență, un program mai complet dezvoltat versiunea răspunsului glenn jackmann ]

Construirea unui tablou asociativ din cheia și valoarea dezbrăcate, folosind prima virgulă ca 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| 

Răspuns

Puteți face o buclă peste matrice și puteți utiliza o variabilă intermediară:

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

Acest lucru atribuie rest porțiunea după prima virgulă; apoi concatenăm trei piese înapoi în original variabilă:

  • porțiunea dinaintea primei virgule
  • o virgulă
  • înlocuirea în rest a fiecărei virgule fără nimic

Comentarii

  • Acesta a fost primul meu gând și este suficient de simplu pentru exemplu, dar aceasta face parte dintr-un script mai mare în care matricea este masivă și există ‘ bucle deja și ar fi un lucru întreg. Acest lucru ar funcționa cu siguranță, dar ar fi foarte dificil de implementat în proiectul mai mare pe care

m lucrez.

  • Destul de corect; Tocmai am încercat să răspund în limitele acestora (numai extinderea parametrilor).
  • Lasă un răspuns

    Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *