W moim skrypcie basha używam find, aby uzyskać nazwy folderów za pomocą symboli wieloznacznych:
for i in $(find ${directory} -mindepth 1 -type d -name ${wildcard}); do stuff=doStuff ${i} done doStuff() { echo ${1} return ${1}"/hello"; }
Problem polega na tym, że kiedy to robię, otrzymuję następujący błąd (powiedzmy, że $ {i} odpowiada „home / me / my_directory”):
line 111: "/home/me/my_directory": is a directory.
(linia 111 to wiersz z „doStuff „)
Próbowałem to zrobić bezskutecznie:
for i in $(find ${directory} -mindepth 1 -type d -name ${wildcard}); do stuff=doStuff "${i}" done
Najwyraźniej dzieje się tak, ponieważ program próbuje wykonać katalogu, ale chcę go tylko jako ciąg znaków, którym mogę manipulować.
Komentarze
Odpowiedź
Cudzysłowy i zastępowanie poleceń to tutaj twoje problemy.
Konkretny problem, z którym się „napotykasz, jest taki, że powłoka próbuje uruchomić polecenie o nazwie /home/me/my_directory
ze zmienną środowiskową stuff=doStuff
.
To, czego naprawdę chcesz (jeśli interpretuję poprawnie), to uruchomienie doStuff
z wartością $i
jako argumentem, przypisując wynik do zmiennej stuff
. Aby to zrobić, należy umieścić polecenie w $()
. Na przykład:
stuff="$(doStuff "$i")"
Zauważ, że wszystko też zostało cudzysłowem. Ma to na celu zapobieżenie dzieleniu przez powłokę na słowa rzeczy, których nie chcesz, aby dzieliły na słowa (spowoduje to zamianę pojedynczego argumentu /foo/bar baz
na /foo/bar
i baz
).
Również wynik Twojej funkcji jest używany jako wartość zwracana, a nie return
.
Ponieważ powinieneś używać więcej cudzysłowów, powinieneś także dodawać je do wszystkiego innego. Oto pełna wersja twojego skryptu:
doStuff() { echo "${1}" >&2 printf "%s/hello" "$1"; } IFS=$"\n" for i in $(find "${directory}" -mindepth 1 -type d -name "${wildcard}"); do stuff="$(doStuff "${i}")" done
Musisz umieścić definicję funkcji, zanim spróbujesz jej użyć. Powłoka nie jest analizowana jak skompilowane programy, gdzie przechodzi przez plik kilka razy. Działa z góry na dół.
Zmodyfikowałem także doStuff
, aby wysłać echo
do STDERR, gdzie zostanie wyświetlony na terminalu, a następnie printf
wysyła do STDOUT, gdzie zostanie przechwycony do zmiennej stuff
.
Zauważ, że nadal będzie to powodować problem, jeśli którykolwiek z katalogów zawiera znak nowej linii. Jest to jednak ograniczenie powłoki. Jedyny znak, którego ścieżka pliku nie może zawiera to znak NULL (\0
). Jednak bash i inne powłoki nie mogą przechowywać znaku NULL w ciągu znaków (nie wszystkie, ale wiele. Wiem, że zsh może), więc możesz ” nie używaj go jako separatora.
Komentarze
- Chciałem tylko dodać, że jeśli masz spację przed / po
=
, to psuje. na przykład MY_VAR = ' / some / path ' jest poprawne, ale MY_VAR = ' / some / ścieżka ' lub MY_VAR = ' / some / path ' nie jest
Odpowiedź
To dobry wysiłek, ale pokazuje, że próbujesz napisać bash
jakby to był język programowania, podczas gdy w rzeczywistości jest to prosty język skryptowy. Problem z twoją konstrukcją stuff=doStuff arg
polega na tym, że bash
nie oznacza tego, co myślisz. Do bash
, stuff=doStuff
to przypisanie zmiennej, a arg
to polecenie. Sugeruję przeczytanie następujących dwóch linków:
- Jak mogę zapisać zwracaną wartość i / lub wynik polecenia w zmiennej?
- Jak zwrócić ciąg (lub dużą liczbę lub liczbę ujemną) z funkcji? „return” pozwala mi podać tylko liczbę od 0 do 255.
Działającą wersją czegoś podobnego do tego, co masz, byłaby
doStuff () { echo "$1/hello" } stuff=$(doStuff "$i")
Odpowiedź
Wiersz podający błąd nie robi tego, co myślisz, że robi:
Najpierw ustawia zmienną środowiskową stuff na łańcuch „doStuff”, a następnie próbuje wykonać $ {i} jako polecenie. A ponieważ $ {i} jest katalogiem, nie powiedzie się z podanym komunikatem o błędzie.
find "${directory}" -mindepth 1 -type d -name "${wildcard}" -print0 | xargs -0 doStuff
//a wrong bash line comment
zamiast#a correct bash line comment