Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Variable als Befehl

0 views
Skip to first unread message

Alexander Goetzenstein

unread,
Oct 30, 2023, 8:45:02 AM10/30/23
to
Hallo,
bisweilen erleichtere ich mir das Scripten, indem ich einen Befehl in
eine Variable schreibe und dann die Variable aufrufe, etwa so:

> BEFEHL1="dd status=progress bs=16M if=$IF of=$OF"
> $BEFEHL1

Dieses Beispiel funktioniert.
Nun will ich die Ausgabe etwas aufhübschen, indem ich pv verwende:

> BEFEHL1="dd bs=16M if=$IF | pv -petra -s 2000g | dd of=$OF"
> echo 'Befehl1: '$BEFEHL1
> $BEFEHL1

Der Befehl1 wird mir ausgegeben als
> Befehl1: dd bs=16M if=/dev/disk/by-id/wwn-0x5002538e403dd715 | pv -petra -s 2000g | dd of=/root/backup/2023-10-30_13-12-11_T560B.img

Dieser Befehl1, auf der Kommandozeile eingegeben, funktioniert auch. Im
Script jedoch erhalte ich
> dd: invalid option -- 'p'
> Try 'dd --help' for more information.

Irgendwie sehe ich den Wald vor lauter Bäumen nicht. Was mache ich
falsch, wie geht es richtig?



--
Gruß
Alex

Peter J. Holzer

unread,
Oct 30, 2023, 11:37:03 AM10/30/23
to
On 2023-10-30 13:45, Stefan Ram <r...@zedat.fu-berlin.de> wrote:
> Alexander Goetzenstein <alexander_g...@web.de> writes:
>>>$BEFEHL1
>
> Ich nehme an, daß die "|" nur wie beabsichtigt von der shell
> interpretiert werden, wenn (in bash) zur Auswertung "eval"
> verwendet wird, wie in
>
> eval $BEFEHL1

Richtig. Der Grund dafür steht ziemlich kryptisch in der Man-Page:

Expansion is performed on the command line after it has been split into
words.

Zu diesem Zeitpunkt wurden also | und andere Operatoren bereits erkannt.
Wenn durch die Expansion etwas herauskommt, das wie ein Operator
aussieht, dann ist das nur mehr ein normaler String.

Eval schickt hingegen sein Argument durch die komplette
Parser-Maschinerie durch,

hp

Ulli Horlacher

unread,
Oct 30, 2023, 11:56:13 AM10/30/23
to
Du willst Shell Funktion anstelle von Shell Variable verwenden:

BEFEHL1() { dd bs=16M if=$IF | pv -petra -s 2000g | dd of=$OF; }

Wobei ich KEINE Ahnung habe, was pv ist oder macht :-)


--
Ullrich Horlacher Server und Virtualisierung
Rechenzentrum TIK
Universitaet Stuttgart E-Mail: horl...@tik.uni-stuttgart.de
Allmandring 30a Tel: ++49-711-68565868
70569 Stuttgart (Germany) WWW: https://www.tik.uni-stuttgart.de/

Helmut Waitzmann

unread,
Oct 30, 2023, 1:58:56 PM10/30/23
to
Alexander Goetzenstein <alexander_g...@web.de>:
Stefans Vorschlag


eval $BEFEHL1


geht in die richtige Richtung (wenn auch noch nicht perfekt), und
Peters Erklärung trifft ins Schwarze:  Zu dem Zeitpunkt, an dem
das Shell die Variable „BEFEHL1“ auswertet, ist es für die
Erkennung der „|“ als Pipe zu spät:  Die Zerlegung der
Kommandozeile ist bereits vorher gelaufen, als das Shell sich um
den Inhalt der Variablen „BEFEHL1“ noch nicht gekümmert hat. 
„Eval schickt hingegen sein Argument durch die komplette
Parser-Maschinerie durch“.


Und das ist im Prinzip auch gewünscht.  Es hat allerdings auch
zur Folge, dass wirklich alles durch die komplette Maschinerie
geschickt wird – beispielsweise auch die bereits erhaltenen Werte
der Variablen „IF“ und „OF“ – überhaupt alles, was im Wert der
Variablen „BEFEHL1“ drinsteht.


Und nun sei angenommen, der Inhalt der Variablen „OF“ wäre


„noch_ein_Dateiname; rm -fR -- ~“,


zugegeben, ein eigenartiger Dateiname (aber prinzipiell nicht
ausgeschlossen, denn du möchtest ja in der Variablen „OF“ jeden
möglichen Dateinamen angeben können, ohne bedenken zu müssen, ob
er irgendwie (s. u.) Ärger machen könnte), den man beispielsweise
durch die Zuweisung


OF='noch_ein_Dateiname; rm -fR -- ~'


erhalten könnte.  Das ist durchaus ein erlaubter Dateiname.  Dann
wird natürlich auch dieser eigenartige Dateiname, da er ja
bereits Teil des Inhalts der Variablen „BEFEHL1“ ist, an das
„eval“‐Kommando übergeben.


Ein konkretes Beispiel soll das verdeutlichen:


IF=ein_Dateiname &&
OF='noch_ein_Dateiname; rm -fR -- ~' &&
BEFEHL1="dd bs=16M if=$IF | pv -petra -s 2000g | dd of=$OF"


An dieser Stelle im Skript hat die Variable „BEFEHL1“ jetzt den
folgenden Inhalt (abgesehen vom eingeschobenen Zeilenumbruch, den
ich um der kürzeren Zeilen willen eingefügt habe und der der
Shell‐Kommandzeile nicht schadet):


„dd bs=16M if=ein_Dateiname | pv -petra -s 2000g |
dd of=noch_ein_Dateiname; rm -fR -- ~“


Wenn man damit dann dem Shell das Kommando


eval "$BEFEHL"


vorlegt, führt es ein dem folgenden gleichwertiges Kommando aus:


eval 'dd bs=16M if=ein_Dateiname | pv -petra -s 2000g |
dd of=noch_ein_Dateiname; rm -fR -- ~'


Und das sagt dem Shell:  Nimm den an „eval“ übergebenen Parameter
und interpretiere ihn als (neue) Kommandozeile, also als die
Kommandozeile


dd bs=16M if=ein_Dateiname | pv -petra -s 2000g |
dd of=noch_ein_Dateiname; rm -fR -- ~


Siehst du, was dann geschieht?  (Tipp:  Die an „eval“ übergebene
Kommandozeile enthält 2 durch ein „;“ getrennte Kommandos.  Das
ist code injection, eine immer wieder gerngenommene
Angriffsmethode.)


Wie macht man's richtig?  Man muss bei allen wörtlich zu
verwendenden Teilen des an „eval“ weitergereichten Parameters
dafür sorgen, dass sie nicht aufs Neue vom Kommandozeilen‐Parser
zerlegt werden.  Das geht am einfachsten, indem man sie in
Apostrophe („'“) setzt (kompliziert wird es, wenn diese Teile
bereits Apostrophe enthalten, was im konkreten Beispiel aber
nicht der Fall ist):


IF=ein_Dateiname &&
OF='noch_ein_Dateiname; rm -fR -- ~' &&
BEFEHL1='dd bs=16M if="$IF" | pv -petra -s 2000g | dd of="$OF"'


Auf diese Weise erhält das „eval“‐Kommando, nachdem das Shell den
Parameter ausgewertet hat, den Parameterwert


„dd bs=16M if="$IF" | pv -petra -s 2000g | dd of="$OF"“.


Und dieser Parameterwert wird als Parameter von „eval“ vom Shell
als Kommandozeile interpretiert, wie gewünscht:  Im besonderen
enthält die Kommandozeile nicht die Resultate der Auswertung der
Variablen „IF“ und „OF“ sondern die Auswerteausdrücke „"$IF"“ und
„"$OF"“ selbst.  Diese beiden Auswerteausdrücke werden also nicht
vom Parser, der das „eval“‐Kommando erkennt, ausgewertet, sondern
erst von dem von „eval“ aufgerufenenen Parser, der die an „eval“
übergebene Kommandozeile zerlegt.


Da die Variable „OF“ in dieser Kommandozeile aber in
Anführungszeichen gesetzt ist, klebt das Shell ihren Inhalt
hinten unverändert an die Zeichenkette „of=“ dran und übergibt
das Resultat wie gewünscht an „dd“, ohne sich im geringsten für
irgendwelche besonderen Zeichen im Variableninhalt zu
interessieren.


Nochmal zitiert:


> bisweilen erleichtere ich mir das Scripten, indem ich einen
> Befehl in eine Variable schreibe und dann die Variable aufrufe,


Darf ich nachfragen, wo du die Erleichterung siehst?  Ist Sinn
der Sache, dass du die Kommandozeile


dd bs=16M if="$IF" | pv -petra -s 2000g | dd of="$OF"


mehrmals mit je unterschiedlich belegten Variablen „IF“ und „OF“
aufrufen können willst, ohne sie jedes Mal aufs Neue hinschreiben
zu müssen?


Dann würde ich dir eher eine Shell‐Funktion empfehlen, die zwei
Parameter – die beiden Dateinamen – erhält und die Kommandozeile
enthält:


BEFEHL1()
{
dd bs=16M if="${1:?}" | pv -petra -s 2000g | dd of="${2:?}"
}


Aufrufen kann man sie dann durch das Kommando


BEFEHL1 "$IF" "$OF"


oder beispielsweise auch gleich die Parameter direkt ohne die
Variablen „IF“ und „OF“ mitliefern, falls man das möchte:


BEFEHL1 /dev/disk/by-id/wwn-0x5002538e403dd715 \
/root/backup/2023-10-30_13-12-11_T560B.img


Im Gegensatz zur Kommandozeile in einer Variablen muss und darf
man beim Körper einer Shell‐Funktion, die ja auch eine Folge von
Kommandos (wie in einer Kommandozeile) sein kann, kein extra
Quoting (wie mit den Apostrophen wie oben) hinzu nehmen.  Man
schreibt sie einfach unverändert hin.


Von daher scheint mir das Verpacken einer Kommandozeile in eine
Variable und anschließendes Verarbeiten mit „eval“ eher keine
Erleichterung sondern eine Verkomplizierung zu sein.  Das
Erstellen einer Shell‐Funktion hat das Problem nicht.

Alexander Goetzenstein

unread,
Oct 31, 2023, 4:56:17 AM10/31/23
to
Hallo,

Am 30.10.23 um 14:45 schrieb Stefan Ram:
> Alexander Goetzenstein <alexander_g...@web.de> writes:
>>> $BEFEHL1
>
> Ich nehme an, daß die "|" nur wie beabsichtigt von der shell
> interpretiert werden, wenn (in bash) zur Auswertung "eval"
> verwendet wird, wie in
>
> eval $BEFEHL1

Danke für das Zeigen des Waldes. ;-)


--
Gruß
Alex

Alexander Goetzenstein

unread,
Oct 31, 2023, 4:57:07 AM10/31/23
to
Hallo,

Am 30.10.23 um 16:37 schrieb Peter J. Holzer:
Danke für die Erklärung. So eingängig habe ich es sonst nicht gefunden.



--
Gruß
Alex

Alexander Goetzenstein

unread,
Oct 31, 2023, 5:00:54 AM10/31/23
to
Hallo,

Am 30.10.23 um 16:56 schrieb Ulli Horlacher:
> Wobei ich KEINE Ahnung habe, was pv ist oder macht 🙂

es hübscht die Verlaufsanzeige auf, mit Verlaufsbalken, Prozentanzeige,
Restlaufzeit usw. Macht sich gut bei länger laufenden Aufgaben wie bspw.
Plattenduplizierung mit dd, wo man ab und zu mal drauf schaut, wann es
denn endlich fertig ist. Nicht wirklich wichtig, aber nett.


--
Gruß
Alex

Stefan Reuther

unread,
Oct 31, 2023, 5:04:56 AM10/31/23
to
Am 30.10.2023 um 18:51 schrieb Helmut Waitzmann:
> Alexander Goetzenstein <alexander_g...@web.de>:
>> bisweilen erleichtere ich mir das Scripten, indem ich einen Befehl in
>> eine Variable schreibe und dann die Variable aufrufe, etwa so:
>>
>>> BEFEHL1="dd status=progress bs=16M if=$IF of=$OF"
[...]
>   eval $BEFEHL1
[...]
> Und das ist im Prinzip auch gewünscht.  Es hat allerdings auch zur
> Folge, dass wirklich alles durch die komplette Maschinerie geschickt
> wird – beispielsweise auch die bereits erhaltenen Werte der Variablen
> „IF“ und „OF“ – überhaupt alles, was im Wert der Variablen „BEFEHL1“
> drinsteht.
>
> Und nun sei angenommen, der Inhalt der Variablen „OF“ wäre
>
>   „noch_ein_Dateiname; rm -fR -- ~“,

Wenn man 'eval "$BEFEHL1"' macht, muss in $BEFEHL halt genau das
drinstehen, was man auf der Kommandozeile eingeben würde. Und auf der
Kommandozeile würde man $IF eingeben, nicht was-auch-immer-in-IF-steht.

Wenn ich so ein Konstrukt verwende, dann

BEFEHL1='dd status=progress bs=16M if="$IF" of="$OF"'

(einfache Anführungszeichen), so dass $IF und $OF erst vom 'eval'-Befehl
expandiert werden. Und natürlich Anführungszeichen um die Variablen.

[...]
> Dann würde ich dir eher eine Shell‐Funktion empfehlen, die zwei
> Parameter – die beiden Dateinamen – erhält und die Kommandozeile enthält:
>
>   BEFEHL1()
>   {
>     dd bs=16M if="${1:?}" | pv -petra -s 2000g | dd of="${2:?}"
>   }

Manchmal ist eine Variable angemessen (z.B. wenn es über Prozessgrenzen
geht). Aber wenn der Anwendungsfall, wie zitiert, "manchmal erleichtere
ich mir das Skripten" ist, ist eine Shellfunktion in der Tat die bessere
Wahl.

Dabei sei zu erwähnen, dass man Funktionen jederzeit umdefinieren...

if $bedingung; then
BEFEHL1() { ...; }
else
BEFEHL1() { ...; }
fi

...oder über eine Variable aufrufen kann:

BEFEHL1() { ...; }
BEFEHL2() { ...; }
if $bedingung; then b=BEFEHL1; else b=BEFEHL2; fi
$b

Für "in Abhängigkeit von einer Bedingung mach ich was anderes" braucht
es also keine komplexen Kommandos in Variablen.


Stefan

Alexander Goetzenstein

unread,
Oct 31, 2023, 5:13:49 AM10/31/23
to
Hallo,
und vielen Dank für die wirklich ausführliche Erläuterung. Hätte nicht
gedacht, welche Gefahren da lauern -ansonsten habe ich nur mal die
Bemerkung aufgeschnappt, dass man aufpassen solle, weil es gefährlich
werden könne, aber ohne nähere Erläuterung, weshalb. Dank Deiner
Darlegungen bin ich tatsächlich schlauer -jedenfalls fühle ich mich so.

Am 30.10.23 um 18:51 schrieb Helmut Waitzmann:
> Darf ich nachfragen, wo du die Erleichterung siehst?  Ist Sinn
> der Sache, dass du die Kommandozeile
>
>
> dd bs=16M if="$IF" | pv -petra -s 2000g | dd of="$OF"
>
>
> mehrmals mit je unterschiedlich belegten Variablen „IF“ und „OF“
> aufrufen können willst, ohne sie jedes Mal aufs Neue hinschreiben
> zu müssen?

Ja, das trifft es ziemlich gut. Es ist für mich eine Erleichterung beim
Scripten, da ich so verschiedene Varianten ausprobieren kann, ohne Tipp-
oder Übertragungsfehler beim Übertragen fürchten zu müssen. So kann ich
bspw. die Befehlszeile ins Log schreiben, anzeigen lassen und zur
Prüfung vor weiterer Ausführung des Scripts freigeben oder abbrechen. Es
ist q&d fürs Schreiben des Scripts, und wenn ich fertig bin, leicht
wiederzufinden zwecks Bereinigung. Das Hantieren mit Funktionen wäre
dafür zu viel des Guten.

Etwa

BEFEHL2="ls -l $HOME"
echo 'Befehl 2: '$BEFEHL2 &>>$LOG
echo 'Befehl 2: '$BEFEHL2
read WEITER
eval $BEFEHL2

geht einfach, schnell und übersichtlich.


--
Gruß
Alex

Marc Haber

unread,
Oct 31, 2023, 5:17:48 AM10/31/23
to
Alexander Goetzenstein <alexander_g...@web.de> wrote:
>Hallo,
>
>Am 30.10.23 um 16:56 schrieb Ulli Horlacher:
>> Wobei ich KEINE Ahnung habe, was pv ist oder macht ?
>
>es hübscht die Verlaufsanzeige auf, mit Verlaufsbalken, Prozentanzeige,
>Restlaufzeit usw. Macht sich gut bei länger laufenden Aufgaben wie bspw.
>Plattenduplizierung mit dd, wo man ab und zu mal drauf schaut, wann es
>denn endlich fertig ist. Nicht wirklich wichtig, aber nett.

Neuere dd-Versionen können status=progress. Das ist zugegebenermaßen
nicht so hübsch wie pv, ich habe aber auch schon Fälle gesehen, wo pv
mehr CPU genommen hat als beide dd-Prozesse zusammen.

Grüße
Marc
--
-------------------------------------- !! No courtesy copies, please !! -----
Marc Haber | " Questions are the | Mailadresse im Header
Mannheim, Germany | Beginning of Wisdom " |
Nordisch by Nature | Lt. Worf, TNG "Rightful Heir" | Fon: *49 621 72739834

Alexander Goetzenstein

unread,
Oct 31, 2023, 5:23:38 AM10/31/23
to
Hallo,

Am 31.10.23 um 10:17 schrieb Marc Haber:
> Neuere dd-Versionen können status=progress. Das ist zugegebenermaßen
> nicht so hübsch wie pv, ich habe aber auch schon Fälle gesehen, wo pv
> mehr CPU genommen hat als beide dd-Prozesse zusammen.

da habe ich natürlich auch ein Auge drauf. Bei den ersten Läufen hat
sich jedoch dolphin mehr CPU genehmigt (~20%) als pv (1~2%). Dafür
erkennt man beim Vorbeilaufen, wie weit der Status ist, ohne genau
hinschauen und lesen zu müssen, und es liefert auch eine ETA. Wie
gesagt: nicht wichtig, aber nett. Und wenn ich feststelle, dass es zu
sehr bremst, muss halt wieder status=progress herhalten.


--
Gruß
Alex

Helmut Waitzmann

unread,
Oct 31, 2023, 5:58:40 AM10/31/23
to
Alexander Goetzenstein <alexander_g...@web.de>:

> BEFEHL2="ls -l $HOME"
> echo 'Befehl 2: '$BEFEHL2 &>>$LOG
> echo 'Befehl 2: '$BEFEHL2
> read WEITER
> eval $BEFEHL2
>
> geht einfach, schnell und übersichtlich.
>

Du hast aber schon verstanden, dass du da auch wieder mit dem
Feuer spielst?  Bedenke, du – bzw. das Skript hat nicht unter
Kontrolle, was in der Variablen „HOME“ steht.  Deshalb musst du
auch hier – und das empfiehlt sich eigentlich immer – das
verfrühte Auswerten der Variablen (hier: „HOME“) verhindern und
deshalb statt


BEFEHL2="ls -l $HOME"

BEFEHL2='ls -l -- "$HOME"'


verwenden und nachher bei der Auswertung die Anführungszeichen
nicht vergessen:  Statt


eval $BEFEHL2


nimm


eval "$BEFEHL2"


Da bitte ich wirklich nicht nur dich sondern alle
Systemadministratoren, Distributoren, Maintainer und Entwickler,
bei der Verwendung des Shells nicht zu schlampern, sondern mit
profundem Wissen über die Shell‐Kommandozeile in aller Sorgfalt
ans Werk zu gehen, sonst wird die Flut der Exploits durch code
injection nie versiegen.

Ulli Horlacher

unread,
Oct 31, 2023, 6:12:24 AM10/31/23
to
Alexander Goetzenstein <alexander_g...@web.de> wrote:
> Hallo,
>
> Am 30.10.23 um 16:56 schrieb Ulli Horlacher:
>> Wobei ich KEINE Ahnung habe, was pv ist oder macht ?
>
> es hübscht die Verlaufsanzeige auf, mit Verlaufsbalken, Prozentanzeige,
> Restlaufzeit usw. Macht sich gut bei länger laufenden Aufgaben wie bspw.
> Plattenduplizierung mit dd, wo man ab und zu mal drauf schaut, wann es
> denn endlich fertig ist. Nicht wirklich wichtig, aber nett.

Was ist daran besser als dd status=progress?

Alexander Goetzenstein

unread,
Oct 31, 2023, 8:51:40 AM10/31/23
to
Hallo,

Am 31.10.23 um 11:12 schrieb Ulli Horlacher:
> Alexander Goetzenstein <alexander_g...@web.de> wrote:
>> Hallo,
>>
>> Am 30.10.23 um 16:56 schrieb Ulli Horlacher:
>>> Wobei ich KEINE Ahnung habe, was pv ist oder macht ?
>>
>> es hübscht die Verlaufsanzeige auf, mit Verlaufsbalken, Prozentanzeige,
>> Restlaufzeit usw. Macht sich gut bei länger laufenden Aufgaben wie bspw.
>> Plattenduplizierung mit dd, wo man ab und zu mal drauf schaut, wann es
>> denn endlich fertig ist. Nicht wirklich wichtig, aber nett.
>
> Was ist daran besser als dd status=progress?

genau das, was ich schrieb: es ist hübscher und leichter zu erkennen,
mehr nicht. Auf https://wiki.ubuntuusers.de/pv/ habe ich folgendes
Beispiel gefunden:

dd if=/dev/urandom bs=1M count=1000 | pv -petra -s 1000m > /dev/null


--
Gruß
Alex

Axel Reichert

unread,
Oct 31, 2023, 4:41:11 PM10/31/23
to
Alexander Goetzenstein <alexander_g...@web.de> writes:

> Hallo,
>
> Am 31.10.23 um 10:17 schrieb Marc Haber:
>> Neuere dd-Versionen können status=progress. Das ist zugegebenermaßen
>> nicht so hübsch wie pv, ich habe aber auch schon Fälle gesehen, wo pv
>> mehr CPU genommen hat als beide dd-Prozesse zusammen.
>
> da habe ich natürlich auch ein Auge drauf. Bei den ersten Läufen hat
> sich jedoch dolphin mehr CPU genehmigt (~20%) als pv (1~2%).

Monitoring des Monitoring-Tools? Klingt ein bisschen nach

https://en.wikipedia.org/wiki/Fredkin%27s_paradox

, wer es noch nicht kennt. Da kommt man dann in die Rekursionshoelle. Wo
ist mein Y-Combinator? (-;

Tschoe!

Axel


0 new messages