substituție greșită care rulează $ {ls … | sed …} cu bash

Am primit următoarea eroare:

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

și acesta este scriptul:

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

Comentarii

Răspuns

${ ... } (acoladele) marchează mai multe un fel de extindere parametru , dintre care cea mai simplă este doar extinderea valorii unei variabile. Lucrurile din interiorul parantezelor din codul dvs. nu sunt „un nume de parametru valid sau orice altă extindere, astfel încât shell-ul se plânge.

Se pare că doriți înlocuirea comenzii, pentru aceasta, sintaxa este $( ... ) (paranteză regulată).

De asemenea, ls din ls $filename | sed... pare puțin inutilă, variabila se extinde la numele fișierului dvs. și ls doar o trece. Puteți folosi echo "$filename" | sed ... în schimb.

Acestea fiind spuse, puteți face aceste modificări direct în shell:

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

sau, utilizând operatorii standard:

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

Dacă rulați sed, vă recomandăm să rețineți că . se potrivește cu orice caracter din regulat expresii, deci ar fi mai corect să o citați cu o bară inversă. De asemenea, puteți da o sed instanță ambele comenzi: sed -e "s/assemblyDB\.//" -e "s/\.las//".

Și apoi for filename in $(find . -type f -name "assemblyDB.*.las"); do are toate problemele pe care le analizează ls , mai ales faptul că spațiul alb și metacaracterele din numele fișierelor îl vor sparge. În ksh / Bash / zsh, puteți face acea buclă întreagă în shell:

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

Comentarii

  • @Jesse_b, da, dar dacă shell-ul dvs. are <<<, probabil că are și ${var/pat/repl}. Dacă rulați sed, puteți da ambele reguli unei singure sed instanță: sed -e 's/assemblyDB\.//' -e 's/\.las//'
  • Am vrut doar să indică UUOE;)

Răspuns

În loc de ${} utilizați backticks ` [Butonul de mai jos Escape de pe tastatura dvs.]

Comentarii

  • Vă rugăm să nu ' nu folosiți backticks. Folosiți înlocuirea comenzii. $( ... )
  • Sunt depășite, sunt greu de citit, nu ' nu cuibăresc și pot întotdeauna să fie înlocuit cu substituirea comenzii paranteză. În plus, sunt greu de plasat în blocuri de coduri inline în majoritatea limbajelor de marcare 🙂
  • Da, este corect. Dar, în ceea ce privește această întrebare, unde a greșit răspunsul meu ??
  • Ați sugerat să folosiți backticks-uri.
  • @Jesse_b Există o mulțime de divuri dintre motivele pentru care nu se utilizează backticks, dar incapacitatea de a le cuibări nu este una dintre ele: echo `echo \`echo hello\`` . Da, necesită evadare, dar asta înseamnă doar că ' nu este la fel de ușor de cuibărit ca $(...), nu că ' este imposibil.

Răspuns

Înlocuirea comenzii (linia 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 

este echivalent, așa cum sa subliniat deja, la (linia 3, nu 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 

sau mai scurt (linia 4 dispărută, acum ecou direct)

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

și comanda sed poate fi redusă la (linia 3)

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

sau poate extrage partea de mijloc cu sed: (linia încă 3)

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

acum find-iterator în loc de for-iterator: (rândul 1 la 3, 4 dispărut)

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 

Dacă numele fișierelor dvs. sunt în o astfel de ordine sau o ordine similară, ar putea funcționa și următoarele:

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 

(8 și 12 incluse, pentru a demonstra fișierele lipsă din secvență).

Lasă un răspuns

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