Ik kreeg de volgende foutmelding:
./assemblyDB.116.las test.sh: line 9: ${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"}: bad substitution
en dit is het script:
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"} echo $no done
Reacties
- Wat probeer je te bereiken met de beugel? Geef ook uw variabelen aan.
- Zie Waarom loopt een lus over de fout van find ' s uitvoer?
Antwoord
${ ... }
(accolades) markeert meerdere soorten parameteruitbreiding , waarvan de eenvoudigste het uitbreiden van de waarde van een variabele is. Het spul tussen accolades in je code is geen “geldige parameternaam of een andere uitbreiding, dus klaagt de shell.
Je lijkt in plaats daarvan een commando te willen vervangen, daarvoor is de syntaxis $( ... )
(normaal haakje).
Ook de ls
in ls $filename | sed...
lijkt een beetje onnodig, de variabele wordt uitgebreid naar uw bestandsnaam, en ls
geeft het gewoon door. U kunt in plaats daarvan gewoon echo "$filename" | sed ...
gebruiken.
Dat gezegd hebbende, je zou die aanpassingen rechtstreeks in de shell kunnen doen:
no="${filename/assemblyDB.}" # remove first match no="${no/.las}"
of, met behulp van de standaard operatoren:
no="${filename#assemblyDB.}" # remove from start of string no="${no%.las}" # remove from end of string
Als u sed
uitvoert, moet u er rekening mee houden dat .
overeenkomt met elk willekeurig teken in normale expressies, dus het zou juister zijn om het te citeren met een backslash. Je kunt ook een sed
instantie beide opdrachten geven: sed -e "s/assemblyDB\.//" -e "s/\.las//"
.
En dan for filename in $(find . -type f -name "assemblyDB.*.las"); do
heeft alle problemen die parsing van ls heeft, meestal het feit dat witruimte en jokertekens in bestandsnamen het breken. In ksh / Bash / zsh zou je die hele lus in de shell kunnen doen:
shopt -s globstar # in Bash for filename in **/assemblyDB.*.las; do ...
Reacties
Antwoord
In plaats van ${}
gebruik backticks `
[De knop onder Escape op je toetsenbord.]
Reacties
- Gelieve niet ' geen backticks gebruiken. Gebruik opdrachtvervanging.
$( ... )
- Ze zijn verouderd, ze zijn moeilijk te lezen, ze ' nesten niet, en ze kunnen altijd worden vervangen door vervanging van het commando tussen haakjes. Bovendien zijn ze in de meeste markdown-talen moeilijk in inline codeblokken te plaatsen 🙂
- Ja, dat klopt. Maar met betrekking tot deze vraag, waar ging mijn antwoord fout ??
- U stelde voor om backticks te gebruiken.
- @Jesse_b Daar ' is er genoeg van redenen om geen backticks te gebruiken, maar het onvermogen om ze te nesten is er niet een van:
echo `echo \`echo hello\``
. Ja, het vereist ontsnapping, maar dat betekent alleen dat het ' niet zo gemakkelijk te nesten is als$(...)
, niet dat het ' s onmogelijk.
Antwoord
Opdrachtvervanging (regel 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
is equivalent, zoals reeds aangegeven, met (regel 3, geen 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
of korter (regel 4 verdwenen, echo nu direct)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//" | sed "s/.las//" done
en het sed-commando kan worden verminderd to (line 3)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//;s/.las//" done
of misschien extraheer het middelste gedeelte met sed: (still line 3)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed -r "s/.*assemblyDB.(.*).las/\1/"" done
nu vind-iterator in plaats van for-iterator: (regel 1 tot 3, 4 verdwenen)
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
Als uw bestandsnamen in een dergelijke of vergelijkbare volgorde, kan het volgende ook werken:
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 en 12 inbegrepen, om ontbrekende bestanden in de reeks aan te tonen).
<<<
heeft, heeft deze waarschijnlijk ook${var/pat/repl}
. Als je sed uitvoert, kun je beide regels gewoon aan éénsed
instantie geven:sed -e 's/assemblyDB\.//' -e 's/\.las//'