Otrzymałem następujący błąd:
./assemblyDB.116.las test.sh: line 9: ${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"}: bad substitution
a to jest skrypt:
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename no=${ls $filename | sed "s/assemblyDB.//" | sed "s/.las//"} echo $no done
Komentarze
- Co próbujesz osiągnąć za pomocą aparatu ortodontycznego? Cytuj również swoje zmienne.
- Zobacz Dlaczego pętla wokół wyjścia find ' jest niewłaściwa? >
Odpowiedź
${ ... }
(nawiasy klamrowe) oznacza kilka rodzaje rozwijania parametrów , z których najprostszym jest po prostu rozszerzenie wartości zmiennej. Rzeczy wewnątrz nawiasów klamrowych w twoim kodzie to nie „ta poprawna nazwa parametru, ani żadne inne rozwinięcie, więc powłoka narzeka.
Wydaje się, że chcesz zamiast tego podstawiać komendy, w tym celu składnia to $( ... )
(zwykły nawias).
Ponadto ls
w ls $filename | sed...
wydaje się trochę niepotrzebne, zmienna rozwija się do nazwy pliku i ls
po prostu ją przekazuje. Zamiast tego możesz po prostu użyć echo "$filename" | sed ...
.
To powiedziawszy, możesz wprowadzić te modyfikacje bezpośrednio w powłoce:
no="${filename/assemblyDB.}" # remove first match no="${no/.las}"
lub używając standardowych operatorów:
no="${filename#assemblyDB.}" # remove from start of string no="${no%.las}" # remove from end of string
Jeśli uruchomisz sed
, możesz zauważyć, że .
pasuje do dowolnego znaku w zwykłym wyrażeń, więc bardziej poprawne byłoby zacytowanie go z ukośnikiem odwrotnym. Możesz także podać jednej sed
instancji obu poleceń: sed -e "s/assemblyDB\.//" -e "s/\.las//"
.
A następnie for filename in $(find . -type f -name "assemblyDB.*.las"); do
ma wszystkie problemy z parsowaniem ls , głównie fakt, że białe znaki i symbole wieloznaczne w nazwach plików to załamują. W ksh / Bash / zsh możesz zrobić całą pętlę w powłoce:
shopt -s globstar # in Bash for filename in **/assemblyDB.*.las; do ...
Komentarze
Odpowiedź
Zamiast ${}
użyj lewych znaczników `
[Przycisk poniżej Escape na klawiaturze.]
Komentarze
- Proszę nie ' nie używaj lewych apostrofów. Użyj podstawiania poleceń.
$( ... )
- Są nieaktualne, trudne do odczytania, nie ' t zagnieżdżają się i nie mogą zawsze być zastępowane przez podstawianie poleceń w nawiasach. Dodatkowo trudno je umieścić w blokach kodu wbudowanego w większości języków przecen 🙂
- Tak, to prawda. Ale jeśli chodzi o to pytanie, gdzie moja odpowiedź poszła źle ??
- Zasugerowałeś użycie odwrotnych znaków.
- @Jesse_b Istnieje ' wiele powodów, by nie używać grawisów, ale brak możliwości ich zagnieżdżenia nie jest jednym z nich:
echo `echo \`echo hello\``
. Tak, wymaga zmiany znaczenia, ale to po prostu oznacza, że ' nie jest tak łatwe do zagnieżdżenia jak$(...)
, a nie że jest niemożliwe.
Odpowiedź
Zastępowanie poleceń (wiersz 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
jest równoważne, jak już wspomniano, (line 3, no 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
lub krócej (linia 4 zniknęła, teraz bezpośrednie echo)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//" | sed "s/.las//" done
i polecenie sed można zmniejszyć do (linia 3)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed "s/assemblyDB.//;s/.las//" done
lub może wyodrębnij środkową część za pomocą sed: (nadal linia 3)
for filename in $(find . -type f -name "assemblyDB.*.las"); do echo $filename echo $filename | sed -r "s/.*assemblyDB.(.*).las/\1/"" done
teraz znajdź-iterator zamiast for-iterator: (linie od 1 do 3, 4 zniknęły)
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
Jeśli twoje nazwy plików są w w takiej lub podobnej kolejności może działać również następująca:
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
(w tym 8 i 12, aby zademonstrować brakujące pliki w sekwencji).
<<<
, prawdopodobnie ma też${var/pat/repl}
. Jeśli uruchomisz seda, możesz po prostu przypisać obie reguły do jednejsed
instancji:sed -e 's/assemblyDB\.//' -e 's/\.las//'