Parametervervanging gebruiken op een Bash-array

Ik heb file.txt die ik moet inlezen in een Bash-array. Dan moet ik spaties, dubbele aanhalingstekens en alles behalve de eerste komma in elk item verwijderen. Hier is hoe ver ik ben gekomen:

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

Wat goed werkt, behalve in de komma-situatie. Ik ben me ervan bewust dat er meerdere manieren zijn om deze kat te villen, maar vanwege het grotere script waar dit deel van uitmaakt, zou ik graag parametervervanging gebruiken om hier te komen:

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

Is dit mogelijk via parametervervanging?

Reacties

  • Is er een reden waarom je de tekst in een array, en waarom je ‘ bijvoorbeeld niet kunt laten awk of sed doen de verwerking van de gegevens?
  • @Jeff – Looping over de array zal een nachtmerrie om te implementeren in het grotere script waar ik ‘ aan werk.
  • @JonRed Ik niet ‘ weet wat je aan het doen bent, dus het ‘ is heel goed mogelijk dat je geen keus hebt, maar in het algemeen, als je merkt dat je zulke complexe snaaracrobatiek in de shell doet, dat ‘ is een zeer goede indicatie dat u een echte programmeertaal zou moeten gebruiken. De shell is niet ontworpen als een programmeertaal, en hoewel het wel als een programmeertaal kan worden gebruikt, is het in feite geen goed idee voor complexere dingen ‘. Ik raad u dringend aan om te overwegen over te schakelen naar perl of python of een andere scripttaal.
  • @terdon Het ‘ is grappig, ik ben net klaar met het zeggen van bijna de exacte hetzelfde tegen mijn collega voordat ik dit bericht las. Ik zei eigenlijk dat dit de laatste versie van dit script is en dat eventuele verdere vereisten herschrijven in Perl noodzakelijk maken. Dus ja, ik ben het er absoluut mee eens

Antwoord

Ik zou verwijderen wat je moet verwijderen met sed voordat in de array wordt geladen (let ook op de variabelen in kleine letters, in het algemeen is het het beste om variabelen met hoofdletters in shellscripts te vermijden):

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

Dit levert de volgende uitvoer op in uw voorbeeldbestand:

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

Als u parameter echt moet gebruiken vervanging, probeer zoiets als dit:

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

Reacties

  • @JonRed Ik heb een versie toegevoegd met parameter vervanging maar het ‘ is complex, omslachtig en lelijk. Dit soort dingen in de shell doen is zelden een goed idee.
  • Merk op dat als je ‘ zowel spaties als dubbele aanhalingstekens hebt verwijderd, deze tekens beschikbaar worden te gebruiken in plaats van je RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda ja, ik heb net je antwoord gelezen. Daar had ik aan moeten denken! Bedankt 🙂
  • Beantwoordt direct de vraag, illustreert waarom mijn voorkeursoplossing niet ‘ t ideaal is, en biedt het meest haalbare alternatief. Jij wint, beste antwoord.

Antwoord

Voor zover ik kan zien, is het niet nodig om lees het in een bash array om die uitvoer te maken:

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

De sed uitdrukking verwijdert spaties en dubbele aanhalingstekens, vervangt de eerste komma door een spatie (er zijn geen andere spaties in de tekenreeks op dit punt), verwijdert alle andere kommas, herstelt de eerste komma, en de voorzetsels en voegt de extra gegevens toe .

Alternatief, met GNU sed:

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

(standard sed ondersteunt de combinatie van 2 en g niet als vlaggen voor de s commando).

Reacties

  • met GNU sed kun je 's/,//2g om kommas te verwijderen, beginnend met de 2e
  • En de laatste 2 s /// commandos kunnen s/.*/|ELEMENT|&|/ maar dat kan meer moeite kosten voor sed.
  • @glennjackman Mogelijk, maar het ziet er nogal netjes uit.
  • Ja, dit is onderdeel van een groter script. De array is nodig, niet alleen voor de uitvoer. Vandaar mijn interesse in parametervervanging. Ik zou hiermee de array kunnen doorlopen, maar dat zal een nachtmerrie zijn om te implementeren. Terndon leverde een lusvrije oplossing met behulp van sed waarop ik ‘ zal terugvallen als parametervervanging een no-go is.
  • If I wasn ‘ t gebonden aan het gebruik van een array, dit zou echter de beste oplossing zijn.

Answer

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

Maak een einde aan de gewoonte om ALLCAPS-variabelenamen te gebruiken. Je zult uiteindelijk in botsing komen met een cruciale “systeemvariabele” zoals PATH en je code breken.

Opmerkingen

  • Geen vervanging van parameters. MAAR, ik wist niet dat ALLCAPS-variabelenamen een slechte gewoonte waren in Bash. Je maakt een goed punt, een punt dat een vluchtige googling zeker bevestigt. Bedankt voor het verbeteren van mijn stijl! 🙂
  • Ik ‘ heb vragen beantwoord waar de persoon PATH=something; ls $PATH schreef en me toen afvroeg over de ls: command not found fout.
  • Er zijn bijna honderd ingebouwde variabelen die in hoofdletters worden genoemd (klik door deze man-pagina link ) om te zien …

Antwoord

[Dit is in wezen een meer volledig ontwikkelde versie van glenn jackmann “s antwoord ]

Een associatieve array bouwen op basis van de gestripte sleutel en waarde, met de eerste komma als scheidingsteken:

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| 

Answer

Je zou de array kunnen doorlopen en een tussenliggende variabele kunnen gebruiken:

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

Dit wijst aan rest het gedeelte na de eerste komma toe; we voegen vervolgens drie stukken weer samen tot het origineel variabele:

  • het gedeelte voor de eerste komma
  • een komma
  • de vervanging in rest van elke komma door niets

Opmerkingen

  • Dit was mijn eerste gedachte en is eenvoudig genoeg voor het voorbeeld, maar dit maakt deel uit van een groter script waar de array enorm is en er ‘ al lussen zijn en het zou een hele zaak zijn. Dit zou zeker werken, maar zou erg omslachtig zijn om te implementeren in het grotere project waar ik ‘ m aan werk.
  • Eerlijk genoeg; Ik heb net geprobeerd te antwoorden binnen de beperkingen (alleen parameteruitbreiding).

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *