slechte vervanging met $ {ls … | sed …} met bash

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

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

  • @Jesse_b, ja, maar als je shell <<< heeft, heeft deze waarschijnlijk ook ${var/pat/repl}. Als je sed uitvoert, kun je beide regels gewoon aan één sed instantie geven: sed -e 's/assemblyDB\.//' -e 's/\.las//'
  • Ik wilde gewoon point out UUOE;)

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).

Geef een reactie

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