
(Diese Frage verwendet das Mac- caffeinate
Tool als Beispiel, aber das Konzept gilt für alle Tools (d. h. xargs
), die ein Dienstprogramm als Argument akzeptieren.)
Das Tool von Mac caffeinate
akzeptiert den Namen eines Dienstprogramms: caffeinate sleep 1
, zum Beispiel (wo sleep
ist das Dienstprogramm). Gibt es eine Möglichkeit, dass es zsh
auch eine Funktion akzeptiert, ohne das Tool selbst zu ändern? Zum Beispiel:
function mysleep {
sleep 2
}
caffeinate mysleep # mysleep: No such file or directory
export -f
Bearbeiten: Diese Frage ist tatsächlich ein Duplikat für Bash – danke, dass Sie mich auf die Antwort hingewiesen haben. Für zsh funktioniert es jedoch nicht. Gibt es eine Möglichkeit, dies in zsh zu tun? (Ich entferne das bash
Tag, um Verwirrung zu vermeiden.)
Antwort1
caffeinate
erwartet die Ausführung eines Befehls in einem neuen Prozess.
Um eine zsh
Funktion zu interpretieren, benötigen Sie einen zsh
Befehl.
Und Sie müssen ihr die Definition dieser Funktion (sowie anderer Funktionen, die sie möglicherweise benötigt) übergeben, beispielsweise mit:
mysleep() {
sleep 2
}
caffeinate zsh -c "$(functions mysleep);mysleep"
functions mysleep
gibt die Definition der mysleep
Funktion aus, die wir zsh
vor dem Aufruf der Funktion zur Interpretation an das neue übergeben, sodass „ zsh
invoved by“ am caffeinate
Ende Folgendes interpretiert:
mysleep() {
sleep 2
};mysleep
Wenn wir mit vergleichen bash
:
mysleep() {
sleep 2
}
export -f mysleep
caffeinate bash -c "mysleep"
(das ist 2 Zeichen kürzer zum Eintippen) bash
reicht aus:
execve("/path/to/caffeinate",
["caffeinate", "bash", "-c", "mysleep"],
["BASH_FUNC_mysleep%%=() { sleep 2\n}", rest-of-environment])
Während zsh
wir mit erhalten:
execve("/path/to/caffeinate",
["caffeinate", "zsh", "-c", "mysleep () {\n\tsleep 2\n};mysleep"],
[rest-of-environment])
Ich sehe mehrere Vorteile dieses letzteren Ansatzes:
- wir haben die volle Kontrolle: wir wissen, wie wir die Funktionsdefinition weitergeben und wie sie verwendet wird. Hier gibt es weniger Spielraum für unangenehme Dinge wie die Art von Shellshock.
- Da der Name der Bash-Umgebungsvariablen, die die Funktionsdefinition enthält,
%
Zeichen enthält (und selbst wenn dies nicht der Fall wäre, denken Siesudo
beispielsweise an), können wir nicht garantieren, dasscaffeinate
sie an den Befehl weitergegeben wird,bash
den sie ausführt. - Wenn es propagiert wird, weil die Funktionsdefinition in envp[] statt in argv[] gespeichert ist, bedeutet dies, dass es die Umgebung jedes anderen Befehls verunreinigt, der in dieser Umgebung ausgeführt wird (einschließlich
sleep
beispielsweise in diesem Beispiel). - (geringfügig) Auch wenn der
bash
Shell-Code kürzer ist, werden mehr Daten übergeben undexecve()
tragen daher stärker zum E2BIG-Limit dieses Systemaufrufs bei.
Wenn Sie die Umgebung nutzen möchten, können Sie weiterhin Folgendes tun:
FUNCS=$(functions mysleep) caffeinate zsh -c '
eval "$FUNCS";mysleep'
In diesem Fall caffeinate
muss es nur ausgeführt werden, caffeinate
während die Funktion ausgeführt wird, und nicht unbedingt, damit die Funktion ausgeführt wird. Wir können andere Ansätze verwenden wie:
mysleep | caffeinate cat
cat
wird so lange ausgeführt, wie mysleep
ausgeführt wird. mysleep
würde jedoch weiterhin in einem separaten Prozess ausgeführt werden und dies wirkt sich auf die Standardausgabe aus mysleep
.
mysleep 3> {fd}>(caffeinate cat)
würde beide Probleme lösen.
Wie oben wird dadurch eine Pipe zwischen mysleep
und erstellt cat
. Das schreibende Ende der Pipe befindet sich jetzt jedoch auf einem neu zugewiesenen Dateideskriptor über 10 (gespeichert in $fd
), in den mysleep
normalerweise nicht geschrieben wird. cat
liest daher nichts, sondern wartet bis zum Dateiende der Pipe, was nur geschieht, wenn mysleep
(und alle untergeordneten Prozesse, die diesen Dateideskriptor erben) beendet wird.
Antwort2
Am besten geht das, indem Sie Ihre Funktion einfach in eine Skriptdatei einfügen und das Skript folgendermaßen aufrufen:
caffeinate myfunction.sh
Inhalt von myfunction.sh:
#!/bin/bash
sleep 2
Stellen Sie sicher, dass die Skriptdatei ausführbar ist. Andernfalls wird ein Berechtigungsfehler angezeigt:
chmod +x myfunction.sh