Script soll Terminal starten und darin ablaufen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
qdox
User
Beiträge: 12
Registriert: Sonntag 6. März 2016, 12:14

Hallo zusammen,

ich arbeite mit Ubuntu und dem Gnome-Dateimanager Nautilus. Im Verzeichnis ~/.gnome2/nautilus-scripts habe ich Scripte abgelegt, die ich aus Nautilus mit einem Rechtsklick auf ein Verzeichnis oder eine Datei aufrufen kann. (Kontextmenu Skripte) Die markierten Verzeichnisse/Dateien werden dabei als Argumente an das Script übergeben.

Nun möchte ich einige der Scripte bei der Ausführung überwachen und ggf. auch Eingaben dazu machen. Ein Terminal ist dafür völlig ausreichend um per print() etwas auszugeben oder per input() eine Abfrage zu machen. (Außerdem habe ich mich noch nicht mit GUIs beschäftigt. Das kommt erst noch.) Die Scripte starten aber nicht in einem Terminal sondern im Hintergrund.

Eine Lösung habe ich schon erarbeitet. Allerdings ist die nicht besonders schön.

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, sys, subprocess
arg = sys.argv
try:
	arg1 = arg[1]
except:
	arg1 = ""
# Def. einer Zeichenkette, die bestimmt nicht in den Argumenten vorkommt.
zsa = "__2.Script-Aufruf__"
if arg1 != zsa:
	# Script wird zum 1. mal aufgerufen und startet sich im Terminal neu.
	arg.insert(1,zsa)
	# Es kann nur 1 Parameter zur Übergabe an gnome-terminal angegeben werden. Jeder Parameter muss mit " umschlossen werden, falls Leerzeichen darin enthalten sind.
	arg = [ "\"" + "\" \"".join(arg) + "\"" ]		
	ter = ["gnome-terminal","-e"]
	ter.extend(arg)
	# Ich war mal in einer Endlosschleife gefange. Daher der folgende (jetzt auskommentierte) input() um das Script zu stoppen und mit ALT+F4 abbrechen zu können.
	#x = input("drück was - irgendwas + enter ")
	subprocess.call(ter)
else:
	# Script wird zum 2. mal aufgerufen, befindet sich im Terminal und wird ausgeführt
	arg.pop(1)
	print(arg)
	print("!" + os.getcwd())
	print("++++++++++++++++++++++++++++++++++++++++++++++++++")
	x = input(" zum beenden 'ENTER' drücken")
Da gibt es doch bestimmt eine Möglichkeit das anders/besser zu machen.

Viele Grüße
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

Ich mag den Ansatz, irgendwelche Terminalfenster zu öffnen nicht. Ein Workaround, wenn du es trotzdem so machen möchtest, ist, dass du im Skript-Verzeichnis von Nautilus ein Shellskript hinterlegst, das den Python-Interpreter in einem Terminalemulator (terminator, gnome-terminal, xterm, o.ä.) mit dem eigentlichen Python-Skript startet und eventuelle Parameter weitergibt:

Code: Alles auswählen

#!/bin/sh
gnome-terminal --command "python /home/benutzer/test.py $1"
(Der Pfad muss absolut angegeben werden)

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
def main():
    print(sys.argv)
    _ = input()

if __name__ == '__main__':
    main()
Benutzeravatar
qdox
User
Beiträge: 12
Registriert: Sonntag 6. März 2016, 12:14

Hallo nezzcarth,

zugegeben, Terminalfenster sind nicht besonders schön, aber manchmal sooo praktisch.

Die Lösung mit 2 Scripts habe ich auch schon angewandt, das ist mit aber zu unflexibel. Da kann ich die Scripts nicht mal eben in einen anderen Ordner verschieben sondern muss sie jedes mal anpassen.

Und dann die Übergabe von Parametern. Kaum ist ein Leerzeichen im Dateinamen oder Pfad, schon muss jeder Parameter separat mit Anführungszeichen versehen werden. Das geht mit Python einfach schöner. Deshalb möchte ich auf Shell-Scripte verzichten.

Zu meiner Frage, ob es auch einfacher geht: Kaum hatte ich die Frage hier gestellt, schon kam mir selbst eine Idee. Die musste ich aber erst ausprobieren.

Warum über die übergebenen Parameter prüfen, wie der Script-Aufruf erfolgte? Warum nicht einfach direkt prüfen, ob ein Terminal läuft? Und das geht über die Umgebungsvariable "TERM".

Wie das Script auf einem System ohne grafische Oberfläche und damit ohne Terminalfenster reagiert kann ich nicht sagen. So ein System habe ich aber nicht und werde es vermutlich auch nie haben. Wenn jemand möchte kann er das aber gerne ausprobieren und mitteilen. Ich bin für jede Info dankbar und lerne gerne dazu.

Hier nun noch das gekürzte Script.

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, sys, subprocess
termtest = str(os.getenv("TERM"))
arg = sys.argv
if termtest == "None":
	# Script  ist nicht im Terminal; startet sich im Terminal neu.
	# Es kann nur 1 Parameter zur Übergabe an gnome-terminal angegeben werden. Jeder Parameter muss mit " umschlossen werden, falls Leerzeichen darin enthalten sind.
	arg = [ "\"" + "\" \"".join(arg) + "\"" ]		
	ter = ["gnome-terminal","-e"]
	ter.extend(arg)
	subprocess.call(ter)
else:
	# Script befindet sich im Terminal und wird ausgeführt
	print(arg)
	print("!" + os.getcwd())
	print("++++++++++++++++++++++++++++++++++++++++++++++++++")
	x = input(" zum beenden 'ENTER' drücken")
Viele Grüße
BlackJack

@qdox: Muss man sich denn überhaupt mit Argumenten beschäftigen? Nach dem was ich mit Netz an Informationen gefunden habe, werden die ausgewählten Dateien als Umgebungsvariablen gesetzt.

Das Problem mit den Anführungszeichen kann ich nicht nachvollziehen. Das geht wenigstens, während Dein zusammenbasteln des ``-e``-Arguments eher problematisch ist. Lass da mal Anführungszeichen oder irgendwelche Zeichen mit besonderer Bedeutung für die Shell drin vorkommen und schon fällt das auf die Nase. Bevor man sich da etwas selber bastelt: `pipes.quote()` existiert.

Wenn die Skripte im gleichen Ordner liegen sollte man da keine Pfade anpassen müssen. Man kann den ja in der Regel aus Element 0 der Argumente ermitteln mit ``dirname "$0"``.

Was soll denn das Umwandeln von dem Wert der an `termtest` gebunden wird in eine Zeichenkette? Es wäre einfacher und auch weniger überraschend wenn man den normalen Weg geht und auf Identität, oder wenigstens auf gleichheit, mit dem Wert `None` prüft als das in eine Zeichenkette umzuwandeln und dann gegen die Zeichenkette mit dem Wort 'None' zu prüfen. Da der Wert an sich aber überhaupt gar nicht verwendet wird, wäre es *noch* einfacher und direkter zu prüfen ob 'TERM' in `os.environ` enthalten ist.

Was Du unbedingt noch absichern musst ist das Skript gegen ein Ende durch Ausnahmen ohne das am Ende auf den Benutzer beziehungsweise eine Eingabe von ihm, gewartet wird, denn dann schliesst sich das Fenster ohne das man eine Möglichkeit hätte zu lesen warum es beendet wird.

Was Du da versuchst ist ziemlich ungewöhnlich. Frag Dich mal warum das so ist. Normalerweise wird hier eine GUI verwendet. In Shellskripten oft mit ``gdialog`` oder ``zenity``. In Python hat man ganze GUI-Rahmenwerke zur Verfügung.
Benutzeravatar
qdox
User
Beiträge: 12
Registriert: Sonntag 6. März 2016, 12:14

Hallo BlackJack,

in der BASH muss man sich mit den Argumenten beschäftigen, wenn man sie an ein weiteres Script übergebenen will. Da bin ich anfangs regelmäßig auf die Nase gefallen, weil immer wieder Leerzeichen auftauchen, die dann einen Dateinamen in 2 teilen und dann kann natürlich nicht s funktionieren. In welcher Form werden die Argumente denn als Umgebungsvariablen gespeichert? Meinst du sys.argv? Wenn sich das Script selbst aufruft, müssen die wieder weiter gegeben werden. Sonst sind sie doch weg.

Das -e-Argument hat gar keine Anführungszeichen. Es steht als String in der Liste ter. Würde ich die Anführungszeichen weglassen, würde Python eine Variable -e erwarten. Popen erwartet ja eine Liste.

Wegen der Anführungszeichen in den Argumenten muss ich mir mal Gedanken machen. Aber eins nach dem anderen. Wobei ich es mal schnell ausprobiert haben. Die können tatsächlich in Dateinamen vorkommen. (Bei mir nicht. Ich mache so was nicht. Aber manchmal bekommt man auch Dateien von auswärts.)

pipes.quote()? Kenn ich nicht. Schau ich mir an.

Scripte im gleichen Ordner sind nicht wirklich eine Lösung. Alle Scripte im Ordner ~/.gnome2/nautilus-scripts werden bei Rechtsklick angezeigt. Das macht die Sache nur unübersichtlich, wenn da Scripte auftauchen, die nicht ausgewählt werden dürfen.

Die Umwandlung von None in einen String habe ich gemacht, da mir das Script beim Vergleich sonst abbricht. Egal ob ich auf None, none oder NONE geprüft habe. Also kurzerhand zu String umwandeln und es funktioniert. Seltsam, aber so ist es passiert.
Auf die Idee os.environ auf TERM zu prüfen, bin ich nicht gekommen. Das werde ich noch probieren. Damit schrumpft der Code wieder ein wenig.

Script absichern gegen Ende durch Ausnahmen. Guter Hinweis. Sonst ist der Aufruf eines Terminals sinnlos. Nächstes Thema, an das ich mich ran mache.

Ungewöhnlich passt zu mir. Einfach kann ja jeder. ;-) Ich habe schon vieles gemacht, was angeblich nicht geht. Nur nicht in Python, das ist noch neu für mich.
Und neu ist genau das Thema, warum ich keine GUI nehme. Zenity und YAD sind mit vertraut aber unter Python erschlägt mich im Moment noch die Vielfalt und so ganz einfach zu nutzen sind die Werkzeuge dazu auch nicht. Sich da einzuarbeiten dauert seine Zeit. Und im Moment will ich erst mal sehen, dass ich die Funktionen die ich brauche zum Laufen bringe. Da ist das Terminal völlig ausreichend. Die Schönheit kommt später. Und wenn ich dann alles nochmal machen muss, habe ich wenigstens was dabei gelernt.

Vielen Dank fürs Feedback und noch einen schönen Sonntag.
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

qdox hat geschrieben: In welcher Form werden die Argumente denn als Umgebungsvariablen gespeichert? Meinst du sys.argv?
Da hatte ich gar nicht nach geschaut, aber BlackJack hat recht. Nautilus definiert eine Hand voll Umgebungsvariablen beim Scriptaufruf:

Code: Alles auswählen

#!/bin/bash
SCRIPTS=$(dirname $0)
gnome-terminal --command "python $SCRIPTS/test.py"

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import os
import time

NAUTILUS_FILE = 'NAUTILUS_SCRIPT_SELECTED_FILE_PATHS'

def main():
    try:
        selected_files = [
            path for path in os.environ[NAUTILUS_FILE].strip().split('\n')]
    except KeyError:
        print('No file(s) selected')
        time.sleep(3)
        sys.exit(1)
    print(selected_files)
    _ = input()

if __name__ == '__main__':
    main()
Das ist sogar noch wesentlich komfortabler. :)
Scripte im gleichen Ordner sind nicht wirklich eine Lösung. Alle Scripte im Ordner ~/.gnome2/nautilus-scripts werden bei Rechtsklick angezeigt. Das macht die Sache nur unübersichtlich, wenn da Scripte auftauchen, die nicht ausgewählt werden dürfen.
Es werden nur Skripte angezeigt, für die das executable Flag gesetzt ist; wenn du die Pythonskripte also als Parameter an den Interpreter übergibst, statt sie direkt zu starten, sollte das doch eigentlich unproblematisch sein.
Terminalfenster sind nicht besonders schön, aber manchmal sooo praktisch.
Ich habe nichts gegen Terminalfenster. Ich finde nur Programme, die sich selbst in einem solchen starten seltsam.
BlackJack

@qdox: Die Umgebungsvariablen haben den Vorteil das man die an Unterprozesse nicht weiterreichen und dabei gegebenfalls verändern/vor der Shell schützen muss.

Mit dem ``-e``-Argument meinte ich natürlich den Wert der Option, also das Argument nach dem '-e'. Sorry wegen der sprachlichen ungenauigkeit, aber ich dachte das kann man sich denken, denn *da* bastelst Du mit Anführungszeichen herum.

Und es sind nicht nur Anführungszeichen um die Du Dir sorgen machen musst, sondern alles was in einer Shell eine besondere Bedeutung hat, kann Probleme bereiten. Aber wie schon gesagt gibt es dafür schon eine fertige Lösung in der Standardbibliothek.

Dann pack die Skripte die nicht im Menü erscheinen sollen halt in einen Unterordner (falls die dann nicht angezeigt werden), oder sonst irgendwo relativ zum Startskript. Gibt es denn nur diese eher primitive Methode? Bei KDE gibt es die Möglichkeit solche Menüs für Dolphin (und Konqueror) über `.desktop`-Dateien zu beschreiben.

Ein vergleich mit `None` sollte nicht zum Abbruch führen:

Code: Alles auswählen

In [27]: os.getenv('XYZ') == None
Out[27]: True

In [28]: os.getenv('XYZ') is None
Out[28]: True

In [29]: os.getenv('TERM') is None
Out[29]: False
`none` oder `NONE` geht natürlich nicht weil Python auf Gross-/Kleinschreibung wert legt und das andere Namen sind als `None`.

Es geht nicht darum dass das mit dem Terminal starten (angeblich) nicht geht, sondern dass das niemand macht weil es umständlich ist und Nachteile hat und keine Vorteile gegenüber einer GUI.
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

BlackJack hat geschrieben: Dann pack die Skripte die nicht im Menü erscheinen sollen halt in einen Unterordner (falls die dann nicht angezeigt werden), oder sonst irgendwo relativ zum Startskript. Gibt es denn nur diese eher primitive Methode? Bei KDE gibt es die Möglichkeit solche Menüs für Dolphin (und Konqueror) über `.desktop`-Dateien zu beschreiben.
@BlackJack: Mag sein, dass das nicht besonders elaboriert ist; aber es ist recht komfortabel und einfach zu benutzen. :) Alle ausfühbaren Dateien, die in ~/.local/share/nautilus liegen, werden unter dem Kontextmenüpunkt "Skripte" aufgeführt, ausführbare Dateien in Unterordnern entsprechend in Untermenüs. Dein Vorschlag funktioniert mit versteckten Ordnern, oder wenn man die betreffende Datei einfach nicht ausführbar macht.

Um beliebige Änderungen am Menü vorzunehmen, ist es meines Wissens nach notwendig, eine Extension zu schreiben (wofür übrigens auch ein Python-Modul existiert). Falls es noch eine andere Möglichkeit gibt, würde mich das auch interessieren.
Antworten