Jag fick följande fel:
./assemblyDB.116.las test.sh: line 9: ${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"}: bad substitution
och detta är 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
- Vad försöker du åstadkomma med hängslen? Citera också dina variabler.
- Se Varför är looping över hitta ' s utgång dålig praxis?
Svar
${ ... }
(lockiga hakparenteser) markerar flera sorter av parameterutvidgning , varav det enklaste är att bara utvidga värdet på en variabel. Grejerna inom hängslen i din kod är inte ett giltigt parameternamn eller någon annan expansion, så skalet klagar.
Du verkar vilja byta kommando istället, för det är syntaxen $( ... )
(vanlig parentes).
Dessutom verkar ls
i ls $filename | sed...
onödigt expanderar variabeln till ditt filnamn och ls
skickar bara igenom den. Du kan bara använda echo "$filename" | sed ...
istället.
Med detta sagt kan du göra dessa ändringar direkt i skalet:
no="${filename/assemblyDB.}" # remove first match no="${no/.las}"
eller med standardoperatörerna:
no="${filename#assemblyDB.}" # remove from start of string no="${no%.las}" # remove from end of string
Om du kör sed
kanske du vill notera att .
matchar alla tecken i vanlig uttryck, så det skulle vara mer korrekt att citera det med en backslash. Du kan också ge en sed
instans båda kommandona: sed -e "s/assemblyDB\.//" -e "s/\.las//"
.
Och sedan for filename in $(find . -type f -name "assemblyDB.*.las"); do
har alla problem parsing ls har, mestadels det faktum att blanksteg och jokertecken i filnamn kommer att bryta det. I ksh / Bash / zsh kan du göra hela slingan i skalet:
shopt -s globstar # in Bash for filename in **/assemblyDB.*.las; do ...
Kommentarer
Svar
I stället för ${}
använd backticks `
[Knappen nedan Escape på tangentbordet.]
Kommentarer
- Vänligen don ' använd inte backticks. Använd kommandosubstitution.
$( ... )
- De är föråldrade, de är svåra att läsa, de <
t häckar och de kan alltid ersättas med parantesbyte. Dessutom är de svåra att placera i inbyggda kodblock på de flesta markdown-språk 🙂
echo `echo \`echo hello\``
. Ja, det behöver fly, men det betyder bara att ' inte är lika lätt att bo som $(...)
, inte att det ' är omöjligt. Svar
Kommandosubstitution (rad 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
motsvarar, som redan påpekats, (rad 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 kortare (rad 4 borta, nu ekar direkt)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//" | sed "s/.las//" done
och kommandot sed kan reduceras till (rad 3)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//;s/.las//" done
eller kanske extrahera den mellersta 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
hitta nu iterator istället för for-iterator: (rad 1 till 3, 4 borta)
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
Om dina filnamn finns i sådan eller liknande ordning kan följande också fungera:
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 och 12 ingår, för att visa saknade filer i sekvensen).
<<<
, har det troligen${var/pat/repl}
. Om du kör sed kan du bara ge båda reglerna till ensed
instans:sed -e 's/assemblyDB\.//' -e 's/\.las//'