Shellprogrammierung - Übergabe eines Dateidescriptors an ein Script
Shellprogrammierung - Übergabe eines Dateidescriptors an ein Script
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?
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?
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.
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.
Ich mag die Schreie.
Nee, nee, das läuft schon alles in einer Shell, also ich tippe im Terminal ein:
:
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:
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:
(Hierbei steht %1 für die übergebene Datei. Deskriptor, nehme ich an, nicht /pfad/dateiname, oder?)
:
Code: Alles auswählen
hk@Melina:~$ rapidsvn
Code: Alles auswählen
/home/hk/RapidSVN_prog.sh: Zeile 3: echo: Schreibfehler: Ungültiger Dateideskriptor.
Also: Ich würde ja gerne mal den Befehl sehen, den RapidSVN da zusammenbastelt. Aus den Einträgen in der ~/.RapidSVN:
Code: Alles auswählen
[Preferences]
StandardEditor=/home/hk/RapidSVN_prog.sh
AlwaysStandardEditor=1
StandardEditorArgs=%1
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.wodim hat geschrieben:Code: Alles auswählen
/home/hk/RapidSVN_prog.sh: Zeile 3: echo: Schreibfehler: Ungültiger Dateideskriptor.
Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.
Ich mag die Schreie.
Langsam, langsam, ich bin ja gerade mal fertig mit dem Korrigieren meines letzten Beitrags.Janka hat geschrieben: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.wodim hat geschrieben:Code: Alles auswählen
/home/hk/RapidSVN_prog.sh: Zeile 3: echo: Schreibfehler: Ungültiger Dateideskriptor.

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.)
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.wodim hat geschrieben:Also wenn ich das richtig sehe, hat nicht RapidSVN die Standardausgabe für das Script zugemacht, sondern es gab nie eine?
Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.
Ich mag die Schreie.
Ähm, ich glaub' ich hab' auch wieder mal Schlafstörungen.
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. 

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


Code: Alles auswählen
# /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
}
Nein.Janka hat geschrieben:Zunächst mal: echo '$@' bewirkt, dass $@ ausgeben wird. Du suchst vermutlich "$@"
Code: Alles auswählen
echo $@ >&2
Code: Alles auswählen
/mnt/data/Geraete/HiFiRack/pre/AmpLog/amp_log.asc
Code: Alles auswählen
echo $1 >&2

Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.
Doch. echo $@ ist etwas anderes als echo '$@', wie anderswo von dir selbst geschrieben, und wieder etwas anderes als echo "$@".wodim hat geschrieben:Nein.Janka hat geschrieben:Zunächst mal: echo '$@' bewirkt, dass $@ ausgeben wird. Du suchst vermutlich "$@"Code: Alles auswählen
echo $@ >&2
$ man bash, Abschnitt QUOTING.
Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.
Ich mag die Schreie.
Ja und? Gesucht habe ich lediglich eine Möglichkeit, festzustellen, was da übergeben wird (s. Titel des ganzen Threads hier),Janka hat geschrieben:Doch. echo $@ ist etwas anderes als echo '$@', wie anderswo von dir selbst geschrieben, und wieder etwas anderes als echo "$@".wodim hat geschrieben:Nein.Janka hat geschrieben:Zunächst mal: echo '$@' bewirkt, dass $@ ausgeben wird. Du suchst vermutlich "$@"

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.echo $@ >&2
So sieht mein Script übrigens jetzt aus:
Code: Alles auswählen
#!/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

Danke, die Manpages meide ich gewöhnlich zu Gunsten etwas "lebendigerer" (und überwiegend deutschsprachiger) Seiten, wie eben z.B. dieser hier.Janka hat geschrieben:$ man bash, Abschnitt QUOTING.


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.

http://www.politik-sind-wir.de/showthre ... post106357
Zuletzt geändert von wodim am 03. Mär 2014 1:01, insgesamt 1-mal geändert.
Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.
Aber sobald ein Leerzeichen im Pfad oder Dateinamen auftaucht, wird der String da abgehackt. Bei $1, nicht bei $@.wodim hat geschrieben:liefert (Beispiel):Code: Alles auswählen
echo $@ >&2
Code: Alles auswählen
/mnt/data/Geraete/HiFiRack/pre/AmpLog/amp_log.asc
übrigens auch.Code: Alles auswählen
echo $1 >&2
Wer hat eine plausible Erklärung dafür?
Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.
Hm, will ich. Bin beim ForschenJanka hat geschrieben:Dann willst du keinen Dateidescriptor übergeben, sondern einen Dateinamen.

Code: Alles auswählen
iceweasel /pfad/dat ei name.html

Code: Alles auswählen
gedit /pfad/dat ei 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: Alles auswählen
gedit '/pfad/dat ei name.txt'
Code: Alles auswählen
gedit '$datei'
Wiederum:
Code: Alles auswählen
case $@ in
*.asc) anweisung(en) ;;
-
-
-
esac
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.
Solltest du also auf Grund meiner "Kritik" die beleidigte Leberwurst spielen, dann schau' doch mal wieder hier 'rein, wenn du dich ausgetrotzt hast:wodim hat geschrieben: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.
http://debianforum.de/forum/viewtopic.p ... 85#p979065
So etwa ist meine naive Vorstellung von einer konstruktiven Diskussion zu verstehen.

Mit einem Computer kann man Probleme lösen, die man ohne ihn nicht hätte.
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.wodim hat geschrieben:Also fang' jetzt bloß nicht an, Krümel zu kacken
Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.
Ich mag die Schreie.