schlechte Substitution mit $ {ls … | sed …} mit bash

Ich habe den folgenden Fehler erhalten:

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

und dies ist das Skript:

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

Kommentare

Antwort

${ ... } (geschweifte Klammern) markiert mehrere Arten von Parametererweiterung , von denen die einfachste nur das Erweitern des Werts einer Variablen ist. Das Material in geschweiften Klammern in Ihrem Code ist kein „gültiger Parametername oder eine andere Erweiterung, daher beschwert sich die Shell.

Sie scheinen stattdessen eine Befehlsersetzung zu wollen, dafür lautet die Syntax (reguläre Klammer).

Auch die ls in ls $filename | sed... scheint etwas zu sein Unnötig, die Variable wird zu Ihrem Dateinamen erweitert und ls leitet sie einfach weiter. Sie können stattdessen auch echo "$filename" | sed ... verwenden.

Sie können diese Änderungen jedoch direkt in der Shell vornehmen:

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

oder mithilfe der Standardoperatoren:

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

Wenn Sie sed ausführen, möchten Sie möglicherweise beachten, dass . mit jedem regulären Zeichen übereinstimmt Ausdrücke, daher wäre es korrekter, sie mit einem Backslash zu zitieren. Sie können auch einer sed -Instanz beide Befehle geben: sed -e "s/assemblyDB\.//" -e "s/\.las//".

Und dann for filename in $(find . -type f -name "assemblyDB.*.las"); do hat alle Probleme, die beim Parsen von ls hat, hauptsächlich die Tatsache, dass Leerzeichen und Platzhalter in Dateinamen es beschädigen. In ksh / Bash / zsh können Sie diese gesamte Schleife in der Shell ausführen:

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

Kommentare

  • @Jesse_b, ja, aber wenn Ihre Shell <<< hat, hat sie wahrscheinlich auch ${var/pat/repl}. Wenn Sie sed ausführen, können Sie beide Regeln einfach einer sed -Instanz zuweisen: sed -e 's/assemblyDB\.//' -e 's/\.las//'
  • Wollte nur weise auf UUOE hin;)

Antwort

Anstelle von ${} Verwenden Sie Backticks ` [Die Schaltfläche unter Escape auf Ihrer Tastatur.]

Kommentare

  • Bitte nicht ' Backticks nicht verwenden. Verwenden Sie die Befehlsersetzung. $( ... )
  • Sie sind veraltet, schwer zu lesen, ' nisten nicht und können immer durch Ersetzen durch Klammerbefehle ersetzt werden. Außerdem sind sie in den meisten Markdown-Sprachen schwer in Inline-Codeblöcke einzufügen 🙂
  • Ja, das ist richtig. Aber in Bezug auf diese Frage, wo ist meine Antwort falsch gelaufen?
  • Sie haben vorgeschlagen, Backticks zu verwenden.
  • @Jesse_b Es gibt ' viele Gründe, keine Backticks zu verwenden, aber die Unfähigkeit, sie zu verschachteln, ist nicht einer von ihnen: echo `echo \`echo hello\`` . Ja, es erfordert ein Escapezeichen, aber das bedeutet nur, dass ' nicht so einfach zu verschachteln ist wie $(...), nicht dass es ' ist unmöglich.

Antwort

Befehlssubstitution (Zeile 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 

entspricht, wie bereits erwähnt, (Zeile 3, keine 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 

oder kürzer (Zeile 4 weg, jetzt direkt echo)

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

und der Befehl sed kann reduziert werden bis (Zeile 3)

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

oder extrahieren Sie den Mittelteil mit sed: (noch Zeile 3)

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

Jetzt Find-Iterator statt For-Iterator: (Zeile 1 bis 3, 4 weg)

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 

Wenn Ihre Dateinamen in sind In einer solchen oder einer ähnlichen Reihenfolge funktioniert möglicherweise auch Folgendes:

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 und 12 enthalten, um fehlende Dateien in der Sequenz zu demonstrieren).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.