Zacząłem od tej króliczej nory, aby poznać zasady tworzenia skryptu instalacyjnego w Pythonie. Wybór Pythona był po prostu zakorzeniony w mojej znajomości, podczas gdy jestem pewien, że byłyby lepsze alternatywy dla tego zadania.
Celem tego skryptu była instalacja ROS na maszynie z uruchomionym skryptem, a także skonfigurowanie środowiska catkin. Wskazówki można znaleźć tutaj i tutaj , odpowiednio.
Obecny skrypt jest następujący:
subprocess.call(["sudo", "sh", "-c", "'echo \"deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main\" > /etc/apt/sources.list.d/ros-latest.list'"])
subprocess.call(["sudo", "apt-key", "adv", "--keyserver", "hkp://ha.pool.sks-keyserver.net:80", "--recv-key", "0xB01FA116"])
subprocess.call(["sudo", "apt-get", "update"])
subprocess.call(["sudo", "apt-get", "install", "ros-kinetic-desktop-full", "-y"])
subprocess.call(["sudo", "rosdep", "init"])
subprocess.call(["rosdep", "update"])
subprocess.call(["echo", '"source /opt/ros/kinetic/setup.bash"', ">>", "~/.bashrc", "source", "~/.bashrc"])
subprocess.call(["sudo", "apt-get", "install", "python-rosinstall", "-y"])
mkdir_p(os.path.expanduser('~') + "/catkin_ws/src")
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && catkin_make)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && source devel/setup.bash"])
Gdy skrypt jest aktualnie uruchomiony, pojawia się błąd:
Traceback (most recent call last):
File "setup.py", line 46, in <module>
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
File "/usr/lib/python2.7/subprocess.py", line 523, in call
return Popen(*popenargs, **kwargs).wait()
File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
Sprawdziłem, czy polecenie działa poprawnie po ręcznym wykonaniu z okna terminala i dlatego uważam, że jest to podstawowe nieporozumienie dotyczące sposobu obsługi tego skryptu i jego zakresu w systemie operacyjnym. Częścią, która sprawia mi wiele zamieszania, jest to, że skarży się, że nie jest w stanie zlokalizować podanego katalogu, podczas gdy sprawdziłem, że ten katalog istnieje. Gdy polecenie jest raczej drukowane z pythona i wklejane do okna terminala, nie występują błędy.
źródło
os.chdir()
cwd
argument docall
Odpowiedzi:
Domyślnie
subprocess.call
nie używa powłoki do uruchamiania naszych poleceń, więc nie można wykonywać poleceń powłoki takich jakcd
.Aby użyć powłoki do uruchomienia poleceń, użyj
shell=True
parametru. W takim przypadku zaleca się przekazywanie poleceń jako pojedynczego ciągu, a nie jako listy. Ponieważ jest uruchamiany przez powłokę, której możesz używać~/
na swojej ścieżce:źródło
os.chdir()
?subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))
?shell=True
wywoła domyślną powłokę, którą jest myślnik. Jeśli skrypt, który zawiera OP, zawiera bashizmy, może się złamać. Dodałem edycję do mojej odpowiedzi, alternatywnym rozwiązaniem byłoby jawne wywołanie określonej powłoki. Szczególnie przydatne, jeśli ktoś ma do czynienia ze skryptem cshshell=True
nawet ze stałymi poleceniami otwiera luki w zabezpieczeniach (np. W systemie podatnym na atak może zostać wyzwolony szok). Zasada praktyczna: jeśli możesz uniknąć używaniashell=True
, powinieneś tego unikać. Tencwd
parametr służy właśnie do wykonania takiego połączenia, jakiego chce OP.subprocess.call()
oczekuje listy, przy czym pierwszy element jest oczywiście prawidłowym poleceniem powłoki. Porównaj to na przykład:W twoim przypadku
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
spodziewasz się znaleźć plik binarny, który wygląda tak (uwaga: odwrotny ukośnik oznacza znak spacji):Jest to traktowane jako pojedyncza nazwa, która powinna mieszkać gdzieś w twoim systemie. To, co naprawdę chciałbyś zrobić, to:
Zauważ, że usunąłem nawias wokół przecinka, ponieważ nie ma powodu, aby używać podpowłoki.
EDYCJA :
Jednak w komentarzach progo już wspomniało, że użycie
cd
w tym przypadku jest zbędne. Odpowiedź Floriana również poprawnie wspomina, żesubprocess.call()
nie używa powłoki. Możesz podejść do tego na dwa sposoby. Po pierwsze, możesz użyćsubprocess.call("command string",shell=True)
Innym sposobem jest jawne wywołanie konkretnej powłoki. Jest to szczególnie przydatne, jeśli chcesz uruchomić skrypt wymagający określonej powłoki. W ten sposób możesz:
źródło
call()
nie oczekuje prawidłowego polecenia powłoki; oczekuje znalezienia ścieżki do rzeczywistego pliku wykonywalnego. A wywoływanie autonomicznegocd
niczego nie osiąga: CWD jest zmienną specyficzną dla procesu, która przestaje istnieć po zakończeniu procesu.cd
tutaj nic nie zrobię. . . . Ale jeśli chodzi o „uzasadnione”, nadal uważam, że właściwe jest sformułowanie - jeśli damsubprocess.call()
coś, czego nie można znaleźć, na przykład['ls -l']
, nie będzie to uzasadnioneUżyj
os.chdir()
zamiast tego.Poza problemami wymienionymi w istniejących odpowiedziach nie wolałbym używać
shell=True
, anisubprocess.call()
tutaj, aby zmieniać katalogu.Python ma swój własny sposób na zmianę katalogu w
os.chdir()
(nie zapomnijimport os
).~
(„dom”) można zdefiniować na kilka sposobów, npos.environ["HOME"]
.Powody, dla których wolę to,
shell=True
możesz przeczytać tutajźródło
Pamiętaj, że używanie
os.chdir()
może powodować niezamierzone skutki uboczne, np. Jeśli używasz wielowątkowości .subprocess
wszystkie metody dostarczającwd
argument słowa kluczowego, który uruchomi żądany podproces w tym katalogu, bez wpływu na inne części procesu python.źródło