Shellprogrammierung - Übergabe eines Dateidescriptors an ein Script

Message
Author
wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

Shellprogrammierung - Übergabe eines Dateidescriptors an ein Script

#1 Post by wodim »

Hallo,

zum Hintergund bitte ich, das mal durchzulesen

http://debianforum.de/forum/viewtopic.php?f=29&t=148074

Ich wende mich also an die Experten hier mit meiner Frage: Was mache ich bei der Übergabe der Datei an das Script falsch? Oder gehe ich da von einem prinzipiell falschen Ansatz aus?

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#2 Post by Janka »

Zunächst mal: echo '$@' bewirkt, dass $@ ausgeben wird. Du suchst vermutlich "$@". Und: Wohin sollte das Skript diesen String ausgeben? Skripte öffnen von selbst keine Fenster, das musst du explizit mit xterm -e /bin/echo "$@" o.ä. tun.

Dann willst du keinen Dateidescriptor übergeben, sondern einen Dateinamen. Das ist ein sehr großer Unterschied. Ein Dateidescriptor ist einfach nur eine Zahl, die für einen Ein-/Ausgabekanal in einem Programm steht. Damit willst du normalerweise auf Shell-Ebene nichts zu tun haben, brauchst du für dein Problem auch nicht.

Der Grund für diese Fehlermeldung ist vermutlich, dass dein Skript nicht ausgeführt werden kann. Kannst du es auf der Kommandozeile aufrufen? Wenn ja: Das Sonderzeichen ~ wird nur von Shells ausgewertet, oder das Programm unterstützt es explizit. Gib da bei RapidSVN einen absoluten Pfad an.
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#3 Post by wodim »

Nee, nee, das läuft schon alles in einer Shell, also ich tippe im Terminal ein:
:

Code: Select all

hk@Melina:~$ rapidsvn
RapidSVN startet, ich klicke halt irgendeine Datei an und wähle "Bearbeiten" oder drücke F3. RapidSVN startet das Script, übergibt ihm die Datei (oder eben nicht?), die Fehlermeldung (im selben Terminal) kommt eindeutig vom Script:

Code: Select all

/home/hk/RapidSVN_prog.sh: Zeile 3: echo: Schreibfehler: Ungültiger Dateideskriptor.
Egal, was ich da schreibe, wie gesagt. Ich hab's ja z.B. auch mit $1 probiert (eben das erste übergebene Argument) - dieselbe Fehlermeldung. Offensichtlich wird dort ein Dateideskriptor erwartet, der nicht ankommt.

Also: Ich würde ja gerne mal den Befehl sehen, den RapidSVN da zusammenbastelt. Aus den Einträgen in der ~/.RapidSVN:

Code: Select all

[Preferences]
StandardEditor=/home/hk/RapidSVN_prog.sh
AlwaysStandardEditor=1
StandardEditorArgs=%1
(Hierbei steht %1 für die übergebene Datei. Deskriptor, nehme ich an, nicht /pfad/dateiname, oder?)

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#4 Post by Janka »

wodim wrote:

Code: Select all

/home/hk/RapidSVN_prog.sh: Zeile 3: echo: Schreibfehler: Ungültiger Dateideskriptor.
Ahaa! Also meldet nicht RapidSVN "ungültiger Dateideskriptor", sondern das Shellskript, genauer das echo. Und das liegt vermutlich daran, dass die Standardausgabe dieses Skriptes nicht offen ist - RapidSVN hat sie zugemacht - du kannst an dieser Stelle nichts ausgeben. Versuche die Ausgabe des echo in eine Datei umzuleiten oder in die Standardfehlerausgabe (mittels >&2), die siehst du ja offensichtlich.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#5 Post by wodim »

Janka wrote:
wodim wrote:

Code: Select all

/home/hk/RapidSVN_prog.sh: Zeile 3: echo: Schreibfehler: Ungültiger Dateideskriptor.
Ahaa! Also meldet nicht RapidSVN "ungültiger Dateideskriptor", sondern das Shellskript, genauer das echo. Und das liegt vermutlich daran, dass die Standardausgabe dieses Skriptes nicht offen ist - RapidSVN hat sie zugemacht - du kannst an dieser Stelle nichts ausgeben. Versuche die Ausgabe des echo in eine Datei umzuleiten.
Langsam, langsam, ich bin ja gerade mal fertig mit dem Korrigieren meines letzten Beitrags. :wink:

Also wenn ich das richtig sehe, hat nicht RapidSVN die Standardausgabe für das Script zugemacht, sondern es gab nie eine? (RapidSVN startet das Script, nicht ich. Würde ja zum Testen nichts nützen - wenn ich's aufrufe und ihm meinetwegen /pfad/dateiname als String übergebe, weiß ich, dass "echo $1" das ausgibt.)

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#6 Post by wodim »

Janka wrote:Versuche die Ausgabe des echo in eine Datei umzuleiten oder in die Standardfehlerausgabe (mittels >&2), die siehst du ja offensichtlich.
Jo, das kam auch erst, nachdem ich schon mit Zitat geantwortet hatte. :wink: Werd' ich machen, aber erst mal gut's Nächtle, schlag' ich vor.

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#7 Post by Janka »

wodim wrote:Also wenn ich das richtig sehe, hat nicht RapidSVN die Standardausgabe für das Script zugemacht, sondern es gab nie eine?
Normalerweise "erben" Kindprozesse alle Filedescriptoren des Vaters zum Zeitpunkt des fork(), auch die Standardausgabe. Es ist eine übliche Maßnahme, diese zwischen fork() und exec() zu schließen oder auf andere Descriptoren umzubiegen, z.B. um mit dem Kind zu kommunizieren.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#8 Post by wodim »

Ähm, ich glaub' ich hab' auch wieder mal Schlafstörungen. :wink: Am besten überlegen wir doch mal, was man sich da so zur Laufzeit alles angucken sollte. Da gibt's doch tolle Befehle ...

Hier mal eine meiner "Baustellen", also ein Teil von dem, was ich mir für meine Scripte so aus dem Netz zusammenkopiert und -gebastelt habe (das Problem der Fehlerfreiheit ist ein ebenso interessantes wie ungelöstes in der theoretischen Informatik, hab' ich mal gelesen. Darf mich ja auch seit ein paar Jährchen "Dipl.-Ing. f. Informationstechnik" nennen, manchmal fällt mir das doch wieder ein.) ;) Nutzung auf eigene Verantwortung natürlich, Korrekturen / Verbesserungen dringend erwünscht. Es darf auch gelacht werden. ;)

Code: Select all

# /bash_libs/common

errCancel () {			# Abbruch bei Fehler   		
	if  [ $1 != 0 ]; then beep -l 200 -f 1200; echo Abbruch mit Fehler $1;
	exit $1
fi
}

user_input (){  		# Usereingabe (1 Zeichen ohne Enter)
	beep; read -n 1 -p "$1" x
	echo $x
}


################## Prozesshandling

pid_from_command () { 	# PID eines laufenden Prozesses aus Komandozeile ermitteln

	line=`ps ax | grep "$1" | grep -v 'grep'| grep -v 'S+'`

   # Beispiel ohne "| grep -v 'S+'" (erste Zeile wird gebraucht):
   # 5658 pts/1    SLs+   2:31 mplayer http://mp3.webradio.rockantenne.de:80
   # 5659 pts/1    S+     0:03 mplayer http://mp3.webradio.rockantenne.de:80

	if [ $? == 0 ]; then		
		echo $(echo $line | cut -d" " -f1)
	else
		echo 0 # Prozess nicht gefunden
	fi		
}

kill_from_command () {  # Prozess aus Kommandozeile ermitteln und killen

	pid=`pid_from_command $1`
	kill $pid
	echo $?
}


################## Fensterhandling

wid_from_command () {  	# Fenster-Id aus Kommandozeile ermitteln

### Achtung: wenn z.B. ps ax "icedove" velangt, dann wmctrl "Icedove" ###

	wid=`wmctrl -l -x | grep $1 | cut -d" " -f1`

	if [ $wid != '' ]; then		
		echo $wid
	else
		echo 0 # Kein Fenster gefunden
	fi
}


wid_from_pid () { 		# Fenster-Id aus Prozess-Id ermitteln

	wid=`wmctrl -p -l | grep "$1" | cut -d" " -f1`

	if [ $wid != '' ]; then		
		echo $wid
	else
		echo 0 # Kein Fenster gefunden
	fi
}


recoverWindow () { 		# Fenster wiederherstellen, $1: Fenster-Id

	wmctrl -i -a $1

}


closeWindow () { 		# Fenster schließen, $1: Fenster-Id

	wmctrl -i -c $1

}

closeWindow_wait_for_end () { # Fenster schließen und Warten, bis Prozess beendet
							  # $1: Fenster-Id, $2: Startkommando			
	closeWindow $1

	while [ $? == 0 ]; do 								

		ps ax | grep $2 | grep -v 'grep'

	done
}

################# Dateioperationen 

addLine () {	# String $1 als Zeile an Datei $2 anhängen  			
	echo $str >> $2; errCancel $?
}


delLine () { 	# Zeile mit String $1 aus Datei $2 löschen - Achtung: Wenn Datei nicht gefunden, wird sie angelegt!
	grep -v $1 $2 > /tmp/tempdatei; errCancel $?
	mv /tmp/tempdatei $2; errCancel $?
}



set_date () { # Dateidatum von Datei $1 auf $2 setzen 

# Eingabe $2: dd.mm.yy, touch verlangt yymmdd

touch --date=${2:6:2}${2:3:2}${2:0:2} $1
	
# touch --date=060826 datei
# touch -t 0610140915 datei

}


##################### Strings formatieren

repl_chars () { 					# In String $1 Zeichen $2 durch $3 ersetzen
	echo ${1//$2/$3}
}

date_unformat () {                  # Eingabe: yyyy*mm*dd 
	echo ${1:8:2}.${1:5:2}.${1:0:4}	# Ausgabe: dd.mm.yyyy
}

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#9 Post by wodim »

Janka wrote:Zunächst mal: echo '$@' bewirkt, dass $@ ausgeben wird. Du suchst vermutlich "$@"
Nein.

Code: Select all

echo $@ >&2
liefert (Beispiel):

Code: Select all

/mnt/data/Geraete/HiFiRack/pre/AmpLog/amp_log.asc

Code: Select all

echo $1 >&2
übrigens auch. Wie hieß das doch gleich - "Probieren geht über Studieren" oder so ähnlich ... Jetzt sollte der Rest ein Vergnügen sein. Danke. :wink:
Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#10 Post by Janka »

wodim wrote:
Janka wrote:Zunächst mal: echo '$@' bewirkt, dass $@ ausgeben wird. Du suchst vermutlich "$@"
Nein.

Code: Select all

echo $@ >&2
Doch. echo $@ ist etwas anderes als echo '$@', wie anderswo von dir selbst geschrieben, und wieder etwas anderes als echo "$@".

$ man bash, Abschnitt QUOTING.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#11 Post by wodim »

Janka wrote:
wodim wrote:
Janka wrote:Zunächst mal: echo '$@' bewirkt, dass $@ ausgeben wird. Du suchst vermutlich "$@"
Nein.
Doch. echo $@ ist etwas anderes als echo '$@', wie anderswo von dir selbst geschrieben, und wieder etwas anderes als echo "$@".
Ja und? Gesucht habe ich lediglich eine Möglichkeit, festzustellen, was da übergeben wird (s. Titel des ganzen Threads hier), :wink: und wie das zu handeln ist, damit eben das aufgerufene Programm die übergebene Datei öffnet. Und das war schließlich konkret so zu sehen:
echo $@ >&2
Also hab' ich mich für deinen Tipp bedankt: Mein Script hat als Kindprozess keine Standardausgabe, das also mal auf die Standard - Fehlerausgabe umleiten - damit war alles klar. Schon vergessen? Also fang' jetzt bloß nicht an, Krümel zu kacken oder die eckige Wahrheit rund biegen zu wollen, nur um unbedingt "Recht zu behalten". Mit solchen Leuten zu diskutieren ist nämlich absolut sinnbefreit.

So sieht mein Script übrigens jetzt aus:

Code: Select all

#!/bin/bash
# . /bash_libs/common

# Programm aus RapidSVN starten, s. Eintrag "Standardeditor" in ~/.RapidSVN
# $1: Übergebene Datei (mit Pfad)

#### echo $1 >&2 ###

case $1 in

	*.asc) # LTSpice oder gedit

		liste='TRUE LTSpice FALSE gedit'
		prog=$(zenity --width=250 --height=190 --title="RapidSVN" --text="Spice-Schaltung bearbeiten mit:" --list --radiolist --column="" --column="" $liste) 		

		if [ $prog == 'LTSpice' ]; then

			winpath=${1//'/mnt/data'/'D:'} 					# /mnt/data ist Wine - Laufwerk D
			winpath=${winpath//'/'/'\\'}   					# Slashs durch doppelte Backslashs ersetzen
			wine 'C:\Program Files\LTC\LTspiceIV\scad3.exe' $winpath

		elif [ $prog == 'gedit' ]; then
			gedit $1
		fi
	;;

	# *.bmp|*.jpg)

	*.wav|*.mp*) audacity $1 ;;

	*) # alles andere -> Standardeditor
		gedit $1 
	;;

esac
Das einzige "Problem" noch: LTSpice wird neu gestartet (und wenn's auch schon mit derselben Datei läuft), gedit öffnet in diesem Fall die Datei in einem neuen Tab. Oder präsentiert mir eben den, wenn er schon existiert. So reagiert eben jedes Programm auf seine Weise - von audacity reden wir also lieber gar nicht erst :wink: ...
Janka wrote:$ man bash, Abschnitt QUOTING.
Danke, die Manpages meide ich gewöhnlich zu Gunsten etwas "lebendigerer" (und überwiegend deutschsprachiger) Seiten, wie eben z.B. dieser hier. :wink: Als alter Deutscher halt. :wink:

Spätestens jetzt fände ich es also angebracht, die Administration hier mal über das nächste Auftreten des seit mehr als 10 Jahren netzbekanntesten Trolls zu infomieren. :wink: Aber vorher sich mal über den:

http://www.politik-sind-wir.de/showthre ... post106357
Last edited by wodim on 03. Mar 2014 1:01, edited 1 time in total.
Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#12 Post by wodim »

wodim wrote:

Code: Select all

echo $@ >&2
liefert (Beispiel):

Code: Select all

/mnt/data/Geraete/HiFiRack/pre/AmpLog/amp_log.asc

Code: Select all

echo $1 >&2
übrigens auch.
Aber sobald ein Leerzeichen im Pfad oder Dateinamen auftaucht, wird der String da abgehackt. Bei $1, nicht bei $@.

Wer hat eine plausible Erklärung dafür?
Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#13 Post by wodim »

Janka wrote:Dann willst du keinen Dateidescriptor übergeben, sondern einen Dateinamen.
Hm, will ich. Bin beim Forschen :wink: Praktisch jedes Programm nimmt den nur bis zum ersten Leerzeichen an. Alles, was danach kommt, wird entweder ignoriert oder als zweites [, drittes, viertes, ...] Argument interpretiert. Das äußert sich z.B. so:

Code: Select all

iceweasel /pfad/dat ei name.html
Da will Iceweasel (Firefox für Debian) :wink: ganz korrekt eine Seite mit dem Link "file:///pfad/dat" öffnen, die's natürlich nicht gibt.

Code: Select all

gedit /pfad/dat ei name.txt
Der Editor präsentiert mir drei (natürlich leere) Tabs, überschrieben mit den "Dateinamen" "dat", "ei" und "name.txt".

Na, und so weiter ... Meine selbstgebastelten Funktionen erst noch ...

Also wie lässt sich ein Dateiname (oder allgemein ein String mit Leerzeichen) korrekt als ein Argument übergeben? Im Terminal doch ganz einfach:

Code: Select all

gedit '/pfad/dat ei name.txt'
Im Script funktioniert

Code: Select all

gedit '$datei'
schon wieder nicht mehr, wenn der Wert von $datei ein String mit Leerzeichen ist.

Wiederum:

Code: Select all

case $@ in

	*.asc) anweisung(en) ;;

-
-
-
esac
Das packt's komischerweise. Und das macht das Script / die Shell doch auch nicht alleine, da wird doch auch ein Programm aufgerufen, oder?

Mir schwant langsam: Das Ganze lässt sich doch sicher ganz elegant umgehen, wenn man keinen String, sondern einen Dateideskriptor übergibt (wie offenbar RapidSVN meinem Script) - aber wie?

(Aus höheren Sprachen wie C, VB, ... ist mir Analoges geläufig als "Kontext", "Call by Reference", "Pointer" oder wie auch immer. Und irgendwie muss sich das wohl hinter dem geheimnisvollen Argument %1 verstecken - das sehe ich bei RapidSVN auch beileibe nicht zum ersten Mal) ...
Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.

wodim
Posts: 17
Joined: 01. Mar 2014 17:29
Location: Gilching

#14 Post by wodim »

wodim wrote:Also hab' ich mich für deinen Tipp bedankt: Mein Script hat als Kindprozess keine Standardausgabe, das also mal auf die Standard - Fehlerausgabe umleiten - damit war alles klar. Schon vergessen? Also fang' jetzt bloß nicht an, Krümel zu kacken oder die eckige Wahrheit rund biegen zu wollen, nur um unbedingt "Recht zu behalten". Mit solchen Leuten zu diskutieren ist nämlich absolut sinnbefreit.
Solltest du also auf Grund meiner "Kritik" die beleidigte Leberwurst spielen, dann schau' doch mal wieder hier 'rein, wenn du dich ausgetrotzt hast:

http://debianforum.de/forum/viewtopic.p ... 85#p979065

So etwa ist meine naive Vorstellung von einer konstruktiven Diskussion zu verstehen. :wink:
Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#15 Post by Janka »

wodim wrote:Also fang' jetzt bloß nicht an, Krümel zu kacken
Fehler passieren mir doch auch dauernd und da bin ich froh, wenn mich jemand auf den Irrtum hinweist. Sonst bleibt der nämlich stehen und der nächste der es liest macht ihn nach.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

Post Reply