Unterschied zwischen dem Befehl „command“ und dem Befehl „builtin“ in Shell Scripting

Unterschied zwischen dem Befehl „command“ und dem Befehl „builtin“ in Shell Scripting

Ich verstehe, dass der Befehl commandangegeben ist inneuester POSIX-Standardund builtinist nicht. Mir ist auch klar, dass beide Befehlereguläre eingebaute(d. h. sie können durch benutzerdefinierte Funktionen überschrieben werden). Einige Shells definieren builtin, aber nicht alle (z. B. dashnicht). Ich möchte verstehen, warum builtinin einigen Shells eingeführt wurde.

Nach meinem besten Verständnis builtinwerden erst spezielle und dann normale integrierte Funktionen zurückgegeben, aber erst commandspezielle integrierte Funktionen, dann normale integrierte Funktionen und dann Befehle im Pfad (und der -pSchalter kann mit verwendet werden, commandum anzugeben, dass die von der Shell definierte Vorgabe verwendet werden soll $PATH, falls der Benutzer die geändert hat $PATH).

In sehe ich beispielsweise mkshFolgendes:

(NOTIZ: mkshauf Ubuntu 20.04 aus Repo installiert http://archive.ubuntu.com/ubuntu focal/universe amd64 mksh amd64 58-1)

$ echo $KSH_VERSION
@(#)MIRBSD KSH R58 2020/03/27
$ which -a echo
/usr/bin/echo
/bin/echo
$ which -a printf
/usr/bin/printf
/bin/printf
$ type echo
echo is a shell builtin
$ type printf
printf is /usr/bin/printf
$ command echo 'Hello World!'
Hello World!
$ command printf 'Hello World!\n'
Hello World!
$ builtin echo 'Hello World!'
Hello World!
$ builtin printf 'Hello World!\n'
mksh: builtin: printf: not found
$ sudo cp /usr/bin/printf /usr/bin/printf.backup
$ sudo cp /bin/printf /bin/printf.backup
$ sudo rm /usr/bin/printf
$ sudo rm /bin/printf
rm: cannot remove '/bin/printf': No such file or directory
$ sudo cp /usr/bin/printf.backup ~/printf
$ echo $PATH | sed 's/:/\n/g'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
# ...remainder ommitted here for brevity
$ export PATH=~:$PATH
$ echo $PATH | sed 's/:/\n/g'
/home/my_username
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
# ...remainder ommitted here for brevity
$ command printf 'Hello World!\n'
Hello World!
$ command -p printf 'Hello World!\n'
mksh: printf: inaccessible or not found

Habe ich das richtig verstanden? Oder sind do commandund builtindo genau dasselbe (und wenn ja, warum wurde es builtineingeführt?)? Oder gibt es einen weiteren subtilen Unterschied zwischen commandund builtin?

(Ich habe versucht, auf StackExchange nach einer Antwort zu suchen, aber nichts gefunden. Wenn mir also jemand eine passende Antwort zeigen könnte, wäre ich sehr dankbar.)

AKTUALISIEREN:

Es ist auch erwähnenswert, dass commandund builtindie Suche nach und Verwendung definierter Aliase „überspringen“. Die Aliaserweiterung erfolgt in der Auswertungsreihenfolge der POSIX-Shell vor der Befehlssuche und -auswertung, ebenso wie die Erweiterung von Arithmetik-, Variablen- und Datei-Platzhaltern. Arithmetik-, Variablen- und Platzhalter werden jedoch innerhalb von commandund ausgewertet builtin, Aliase jedoch nicht. Das scheint etwas zu sein, was in den Dokumenten erwähnt werden sollte.

Zum Beispiel:

$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ command echo $((1 + 1))
2
$ builtin echo $((3 + 1))
4

Aber

$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ alias \[='echo hello'
$ [
hello
$ if builtin [ 'north' != 'south' ]; then echo 'what a world!'; fi
what a world!

UPDATE 2:

Ich denke, es ist auch erwähnenswert, dass ich nach einigem Graben und Experimentieren herausgefunden habe, dass zshsich verhältDAS KOMPLETTE GEGENTEILdes POSIX-Standards und anderer Shells im Bourne-Stil in Bezug auf den commandBefehl.

Von demzsh-Dokumente(Abschnitt 6.2, Präbefehlsmodifizierer)

Befehl [-pvV]
Das Befehlswort wird als Name eines externen Befehls und nicht als einer Shell-Funktion oder eines integrierten Befehls betrachtet.

Zum Beispiel

$ echo ${ZSH_VERSION}
5.8
$ command cd ~
zsh: command not found: cd
$ command -p cd ~
zsh: command not found: cd
$ command echo 'hi'
hi
# `echo` is a regular builtin just like `cd` though...??!!!
$ set -o |grep posix
posixaliases          off
posixargzero          off
posixbuiltins         off
posixcd               off
posixidentifiers      off
posixjobs             off
posixstrings          off
posixtraps            off
$ cd() { echo 'I told you.'; }
$ cd
I told you.
# ????!!!!

Nur wenn die POSIX_BUILTINSUmgebungsvariable gesetzt ist (verwenden Sie set -o posixbuiltins), führt der Befehl commandauch spezielle und reguläre Built-Ins aus.

Zum Beispiel

$ echo ${ZSH_VERSION}
5.8
$ cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var
$ set -o posixbuiltins
$ command cd ~
$ ls
Desktop    Downloads  Pictures  Templates
Documents  Music      Public    Videos
$ command -p cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var

Auf der anderen Seite, von derBash-Dokumente

Befehl
Befehl [-pVv]Befehl [Argumente …]

Führt den Befehl mit Argumenten aus und ignoriert dabei alle Shell-Funktionen mit dem Namen „Befehl“. Es werden nur in der Shell integrierte Befehle oder Befehle ausgeführt, die durch Durchsuchen des Pfads gefunden wurden.

Zum Beispiel

$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var
$ set +o posix  # Turn off POSIX mode
$ command cd ~
$ ls
Desktop    Downloads  Pictures  Templates
Documents  Music      Public    Videos
$ command -p cd /
$ ls
bin   dev  home  lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  etc  init  lib32  libx32  media       opt  root  sbin  srv   tmp  var

So zshverhält sichDAS KOMPLETTE GEGENTEILanderer Shells im Bourne-Stil in Bezug auf den commandBefehl ... Ich fange an, sie zshimmer weniger zu mögen ... Käufer aufgepasst (Vorbehalt beim Kauf) Ich nehme an.

UPDATE 3:

Es ist auch erwähnenswert, dass kein integrierter Befehl ksh88vorhanden ist . Dieser wurde in eingeführt . Um einen integrierten Befehl in zu ersetzen , müssten Sie eine umständliche Kombination aus Aliasing, Funktionen und Anführungszeichen verwenden.commandksh93ksh88

(Quelle: Robbins, Arnold; Rosenblatt, Bill. Learning the Korn Shell: Unix Programming (S. 456). O'Reilly Media. Kindle-Ausgabe.)

Dies steht im Einklang mit der Antwort von @Gilles: „Also, hör auf, böse zu sein.“

Antwort1

DerPOSIX-Begründung fürcommandbeantwortet die meisten historischen Aspekte Ihrer Frage.

DerBefehlDienstprogramm ist etwas ähnlich der Eighth Edition ShelleingebautBefehl, aber daBefehlgeht auch in das Dateisystem, um nach Dienstprogrammen zu suchen, der Nameeingebautwäre nicht intuitiv.

(…) DerBefehl -vUnd-VEs wurden Optionen hinzugefügt, um die Anforderungen der Benutzer zu erfüllen, die derzeit von drei verschiedenen historischen Dienstprogrammen erfüllt werden:Typin der System V-Shell,woherin der KornShell undwelchein der C-Shell.

ImAchte Ausgabe shwurde das builtinintegrierte Feature als bloßes Umgehen von Funktionen dokumentiert:

Führen Sie den integrierten Sonderbefehl (z. B. „break“) unabhängig von Funktionen aus, die mit demselben Namen definiert sind.

Aliase gab es noch nicht (und als sie auftauchten, gab es verschiedene Mechanismen, um sie zu umgehen). Wenn Sie eine Funktion umgehen wollten, um einen externen Befehl auszuführen, konnten Sie ihren vollständigen Pfad angeben, was den Vorteil hatte, genau anzugeben, was Sie ausführen wollten, falls sich mehrere ausführbare Dateien mit diesem Namen im Befehlssuchpfad befanden. Portabilität zwischen Systemen, bei denen der vollständige Pfad zu einem Befehl unterschiedlich sein könnte, war kein weit verbreitetes Anliegen. Das Gleiche builtingalt für die eine Sache, die nicht wirklich anders gemacht werden konnte.

Später kam POSIX und fügte eine Standardmethode zum Umgehen einer Funktion hinzu. In diesem Zusammenhang war die Portabilität auf Systeme, auf denen sich externe Befehle an anderen Orten befanden, ein großes Anliegen und reichte daher builtinnicht aus. Daher das neue , commanddas Funktionen (und Aliase, da es an eine Stelle command foostellt foo, an der Aliase nicht erweitert werden) umgeht und Standardbefehle findet. (Außerdem hat ksh heute ein integriertes Element namens , builtindas etwas völlig anderes macht, aber ich weiß nicht, ob es vor oder nach der Erstellung von durch POSIX kam command.) Überspringt jedoch commandabsichtlich keine integrierten Befehle, auch hier aus Portabilitätsgründen: Wenn ein sh-Programm einen Standardbefehl aufruft, entscheidet das Betriebssystem, ob dieser Befehl als integriertes Element bereitgestellt wird. commandhebt das „spezielle integrierte“ Verhalten spezieller integrierter Befehle auf, damit die Anwendung wieder nicht wissen muss, ob sie ein integriertes Element aufruft oder nicht.

Ich weiß nicht, warum zsh commandBuiltins umgeht, wenn es sich nicht im POSIX-Modus befindet (insbesondere, wenn dieposix_builtinsMöglichkeitist nicht gesetzt). Die aktuelle Implementierung commandgeht auf eine Änderung im Mai 1996 zurück, die inzsh 2.6 Beta 20(„-, exec, noglob und command aus der Liste der reservierten Wörter entfernen“). Da diese Implementierung bereits eine andere Handhabung für den POSIX-Modus hatte, gehe ich davon aus, dass dies der Abwärtskompatibilität mit einer früheren Version von zsh diente, habe dies aber nicht weiter untersucht. Es könnte Absicht sein, denn wenn posix_builtinsnicht gesetzt, sind integrierte Funktionen nicht unbedingt mit POSIX kompatibel und es ist daher am besten, sie nicht aufzurufen, wenn eine Anwendung den speziellen POSIX-Befehl verwendet command.

verwandte Informationen