Med hjälp av parameterbyte på en Bash-array

har jag file.txt som jag behöver läsa in i en Bash-array. Då måste jag ta bort mellanslag, dubbla citat och alla utom det första komma i varje post . Här är hur långt jag har kommit:

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

Vilket fungerar bra förutom kommasituationen. Jag är medveten om att det finns flera sätt att hyra den här katten, men på grund av det större skriptet som det här är en del av skulle jag verkligen vilja använda parameterbyte för att komma hit:

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

Är detta möjligt via parameterbyte?

Kommentarer

  • Finns det någon anledning att du behöver behålla texten en matris och varför du inte kan ’ t låta t.ex. awk eller sed gör bearbetningen av data?
  • @Jeff – Looping över matrisen blir en mardröm att implementera i det större skriptet som jag ’ arbetar på.
  • @JonRed Jag vet inte ’ vet inte vad du gör, så det är ’ helt möjligt att du kanske inte har något val i frågan, men i allmänhet, när du befinner dig att göra så komplexa strängakrobatik i skalet, att ’ är en mycket bra indikation på att du ska använda ett verkligt programmeringsspråk. Skalet är inte utformat som ett programmeringsspråk, och även om det kan användas som ett, är det verkligen inte ’ inte en bra idé för mer komplexa saker. Jag uppmanar dig starkt att överväga att byta till perl eller python eller något annat skriptspråk.
  • @terdon Det ’ är roligt, jag har precis gjort det att säga nästan exakt samma sak till min kollega innan jag läste det här inlägget. Jag sa i princip att detta är den slutliga versionen av detta manus och att ytterligare krav kommer att kräva omskrivning i Perl. Så ja, jag håller definitivt med

Svar

Jag skulle ta bort det du behöver ta bort med sed innan laddas in i matrisen (notera också små variabelnamn, i allmänhet är det bäst att undvika stora variabler i skalskript):

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

Detta ger följande utdata i din exempelfil:

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

Om du verkligen måste använda parameter substitution, prova något så här:

#!/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 Jag lade till en version med parameter substitution men den ’ är komplex, besvärlig och ful. Att göra den här typen av saker i skalet är mycket sällan en bra idé.
  • Observera att om du ’ har tagit bort båda mellanslag och dubbla citat blir dessa tecken tillgängliga att använda istället för din RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda ja, jag läste bara ditt svar. Borde ha tänkt på det! Tack 🙂
  • Svarar direkt på frågan, illustrerar varför min föredragna lösning inte är ’ t ideal och ger det mest lönsamma alternativet. Du vinner, bästa svaret.

Svar

Så vitt jag kan se finns det inget behov av läs den i en bash -matris för att skapa den utgången:

$ 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 uttryck raderar mellanslag och dubbla citattecken, ersätter det första kommatecknet med ett mellanslag (det finns inga andra mellanslag i strängen vid denna tidpunkt), raderar alla andra kommatecken, återställer det första kommaet och förbereder och lägger till extra data .

Alternativt med GNU sed:

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

(standard sed stöder inte kombinationen av 2 och g som flaggor till s -kommando).

Kommentarer

  • med GNU sed kan du använda 's/,//2g för att ta bort kommatecken, som börjar med det andra
  • Och de sista 2 s /// kommandona kan vara s/.*/|ELEMENT|&|/ men det kan vara mer ansträngning för sed.
  • @glennjackman Möjligen, men det ser ganska snyggt ut.
  • Ja, det här är en del av ett större manus. Matrisen är nödvändig, inte bara för utdata. Därav mitt intresse för parameterbyte. Jag kunde slingra över arrayen med detta men det blir en mardröm att genomföra. Terndon tillhandahöll en loopfri lösning med sed som jag ’ sannolikt kommer att falla tillbaka på om parameterbyte är ett no-go.
  • Om jag inte var ’ Det är dock bundet till att använda en array, men det här skulle vara den bästa lösningen.

Svar

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

Undvik vanan att använda ALLCAPS-variabelnamn. Du kommer så småningom att kollidera med en viktig ”system” -variabel som PATH och bryta din kod.

Kommentarer

  • Inte parameterbyte. MEN jag var inte medveten om att ALLCAPS-variabelnamn var en dålig vana i Bash. Du gör en bra poäng, en som en kortvarig googling definitivt bekräftar. Tack för att du förbättrade min stil! 🙂
  • Jag ’ har svarat på frågor där personen skrev PATH=something; ls $PATH och undrade sedan om ls: command not found fel.
  • Det finns nästan hundra inbyggda variabler som är namngivna i alla versaler (klicka igenom den här mansidan länk ) för att se …

Svar

[Detta är i huvudsak ett mer fullt utvecklat version av glenn jackmanns svar ]

Bygga en associerande matris från den avdragna nyckeln och värdet med det första kommaet 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 slinga över arrayen och använda en mellanliggande variabel:

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

Detta tilldelar rest delen efter det första komma; vi sammanfogar sedan tre delar tillbaka till originalet variabel:

  • delen före första komma
  • ett komma
  • utbyte i rest av varje komma med ingenting

Kommentarer

  • Det här var min första tanke och är enkelt nog för exemplet men det här är en del av större skript där arrayen är massiv och där ’ redan slingrar och det skulle vara en hel sak. Detta skulle definitivt fungera men skulle vara väldigt besvärligt att genomföra i det större projektet jag ’ arbetar med.
  • Rättvis; Jag försökte bara svara inom begränsningarna (endast parameterutvidgning).

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *