złe podstawienie z $ {ls … | sed …} z bash

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

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

  • @Jesse_b, tak, ale jeśli twoja powłoka ma <<<, prawdopodobnie ma też ${var/pat/repl}. Jeśli uruchomisz seda, możesz po prostu przypisać obie reguły do jednej sed instancji: sed -e 's/assemblyDB\.//' -e 's/\.las//'
  • Po prostu chciałem wskaż UUOE;)

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

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *