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
- Was versuchst du mit den Zahnspangen zu erreichen? Zitieren Sie auch Ihre Variablen.
- Siehe Warum wird die Ausgabe von ' als schlechte Vorgehensweise wiederholt? A. >
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 einersed
-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).