dårlig erstatning som kjører $ {ls … | sed …} med bash

Jeg fikk følgende feil:

./assemblyDB.116.las test.sh: line 9: ${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"}: bad substitution 

og dette er skriptet:

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"} echo $no done 

Kommentarer

Svar

${ ... } (krøllete bukseseler) markerer flere slags parameterutvidelse , den enkleste er bare å utvide verdien til en variabel. Tingene i seler i koden din er ikke et gyldig parameternavn eller annen utvidelse, så skallet klager.

Du ser ut til å ønske å bytte kommando i stedet, for det er syntaksen $( ... ) (vanlig parentes).

Også ls i ls $filename | sed... virker litt unødvendig utvides variabelen til filnavnet ditt, og ls sender den bare gjennom. Du kan bare bruke echo "$filename" | sed ... i stedet.

Når det er sagt, kan du gjøre disse endringene direkte i skallet:

no="${filename/assemblyDB.}" # remove first match no="${no/.las}" 

eller ved å bruke standardoperatørene:

no="${filename#assemblyDB.}" # remove from start of string no="${no%.las}" # remove from end of string 

Hvis du kjører sed, kan det være lurt å merke seg at . samsvarer med hvilket som helst tegn i vanlig uttrykk, så det ville være riktigere å sitere det med tilbakeslag. Du kan også gi en sed begge kommandoene: sed -e "s/assemblyDB\.//" -e "s/\.las//".

Og så for filename in $(find . -type f -name "assemblyDB.*.las"); do har alle problemene parsing ls har, mest det faktum at hvite mellomrom og jokertegn i filnavn vil bryte det. I ksh / Bash / zsh kan du gjøre hele sløyfen i skallet:

shopt -s globstar # in Bash for filename in **/assemblyDB.*.las; do ... 

Kommentarer

  • @Jesse_b, ja, men hvis skallet ditt har <<<, har det sannsynligvis også ${var/pat/repl}. Hvis du kjører sed, kan du bare gi begge reglene til en sed forekomst: sed -e 's/assemblyDB\.//' -e 's/\.las//'
  • Ville bare påpeke UUOE;)

Svar

I stedet for ${} bruk backticks ` [Knappen nedenfor Escape på tastaturet ditt.]

Kommentarer

  • Vennligst ikke ' Ikke bruk backticks. Bruk kommandosubstitusjon. $( ... )
  • De er foreldede, de er vanskelige å lese, de ' t hekker, og de kan alltid erstattes av parentes-kommandosubstitusjon. I tillegg er de vanskelig å plassere i innebygde kodeblokker på de fleste markdown-språk 🙂
  • Ja, det stemmer. Men når det gjelder dette spørsmålet, hvor gikk svaret mitt galt ??
  • Du foreslo å bruke backticks.
  • @Jesse_b ' det er nok av grunner til ikke å bruke backticks, men manglende evne til å rede dem er ikke en av dem: echo `echo \`echo hello\`` . Ja, det krever å rømme, men det betyr bare at det ' ikke er så lett å hekke som $(...), ikke at det ' er umulig.

Svar

Kommandosubstitusjon (linje 3)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=$(ls $filename | sed "s/assemblyDB.//" | sed "s/.las//") echo $no done 

tilsvarer, som allerede påpekt, til (linje 3, ingen ls)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=$(echo $filename | sed "s/assemblyDB.//" | sed "s/.las//") echo $no done 

eller kortere (linje 4 borte, nå ekko direkte)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//" | sed "s/.las//" done 

og kommandoen sed kan reduseres til (linje 3)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//;s/.las//" done 

eller kanskje trekke ut den midtre delen med sed: (still linje 3)

for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed -r "s/.*assemblyDB.(.*).las/\1/"" done 

finn nå iterator i stedet for for iterator: (linje 1 til 3, 4 borte)

find . -type f -name "assemblyDB.*.las" -printf "%f\n" -exec sh -c " echo {} | sed -r "s/.*assemblyDB.(.*).las/\1/"" ";" assemblyDB.11.las 11 assemblyDB.9.las 9 assemblyDB.10.las 10 

Hvis filnavnene dine er i slik eller lignende rekkefølge, kan følgende også fungere:

for i in {8..12} ; do ls assemblyDB.$i.las && echo $i ; done 2>/dev/null assemblyDB.9.las 9 assemblyDB.10.las 10 assemblyDB.11.las 11 

(inkludert 8 og 12, for å demonstrere manglende filer i sekvensen).

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *