Sagen Sie, wenn ich ein Programm mit der folgenden Zeile geschrieben habe:
int main(int argc, char** argv)
Jetzt weiß es, welche Befehlszeilenargumente durch Überprüfen des Inhalts von an es übergeben werden argv
.
Kann das Programm feststellen, wie viele Leerzeichen zwischen Argumenten stehen? Zum Beispiel, wenn ich diese in bash eingebe:
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
Environment ist ein modernes Linux (wie Ubuntu 16.04), aber ich denke, die Antwort sollte für alle POSIX-kompatiblen Systeme gelten.
Antworten:
Es ist nicht sinnvoll, von "Räumen zwischen Argumenten" zu sprechen. Das ist ein Shell-Konzept.
Die Aufgabe einer Shell besteht darin, ganze Eingabezeilen zu Argumenten zusammenzufassen, mit denen Befehle gestartet werden sollen. Dies kann das Parsen von Strings in Anführungszeichen, das Erweitern von Variablen, das Einfügen von Platzhaltern und Tilde-Ausdrücken usw. beinhalten. Der Befehl wird mit einem Standardsystemaufruf gestartet
exec
, der einen Vektor von Zeichenfolgen akzeptiert.Es gibt andere Möglichkeiten, einen Vektor aus Zeichenfolgen zu erstellen. Viele Programme verzweigen sich und führen ihre eigenen Unterprozesse mit vorgegebenen Befehlsaufrufen aus - in diesem Fall gibt es nie so etwas wie eine "Befehlszeile". Ebenso kann eine grafische (Desktop-) Shell einen Prozess starten, wenn ein Benutzer ein Dateisymbol zieht und es auf einem Befehls-Widget ablegt. Auch hier gibt es keine Textzeile, in der Zeichen "zwischen" Argumenten stehen.
Was den aufgerufenen Befehl betrifft, ist das, was in einer Shell oder einem anderen übergeordneten / vorläufigen Prozess vor sich geht, privat und verborgen - wir sehen nur das Array von Zeichenfolgen, das Standard C
main()
akzeptiert.quelle
tar cf texts.tar *.txt
das tar-Programm beim Ausführen zwei Argumente erhält und das zweite (*.txt
) selbst erweitern muss. Viele Leute wissen erst, wie es wirklich funktioniert, wenn sie ihre eigenen Skripte / Programme schreiben, die mit Argumenten umgehen.Im Allgemeinen nicht. Die Befehlszeilenanalyse wird von der Shell durchgeführt, die die nicht analysierte Zeile dem aufgerufenen Programm nicht zur Verfügung stellt. Tatsächlich kann Ihr Programm von einem anderen Programm ausgeführt werden, das das Argument nicht durch Analysieren eines Strings, sondern durch programmgesteuertes Erstellen eines Arrays von Argumenten erstellt hat.
quelle
execve(2)
.Nein, dies ist nur möglich, wenn die Leerzeichen Teil eines Arguments sind.
Der Befehl greift über ein Array auf die einzelnen Argumente zu (je nach Programmiersprache in der einen oder anderen Form), und die tatsächliche Befehlszeile kann in einer Verlaufsdatei gespeichert werden (sofern sie an einer interaktiven Eingabeaufforderung in einer Shell mit Verlaufsdateien eingegeben wird) niemals in irgendeiner Form an den Befehl weitergegeben.
Alle Befehle unter Unix werden am Ende von einer
exec()
der Funktionsfamilien ausgeführt. Diese enthalten den Befehlsnamen und eine Liste oder ein Array von Argumenten. Keiner von ihnen verwendet eine Befehlszeile, wie sie an der Shell-Eingabeaufforderung eingegeben wurde. Diesystem()
Funktion funktioniert, aber ihr String-Argument wird später von ausgeführtexecve()
, das wiederum ein Array von Argumenten anstelle eines Befehlszeilen-Strings verwendet.quelle
hello
undworld
ist buchstäblich ein Leerzeichen zwischen den beiden Argumenten.hello
undworld
ist buchstäblich die zweite von drei Argumente zu liefern.Im Allgemeinen ist es nicht möglich, wie mehrere andere Antworten erklären.
Allerdings Unix - Shells sind gewöhnliche Programme (und sie interpretieren die Befehlszeile und Globbing es, dh Erweiterung des Befehls , bevor Sie
fork
&execve
für sie). Siehe diese Erklärung zubash
Shell-Operationen . Sie könnten Ihre eigene Shell schreiben (oder eine vorhandene freie Software- Shell, z. B. GNU bash, patchen ) und diese als Ihre Shell verwenden (oder sogar Ihre Login-Shell, siehe passwd (5) & shells (5) ).Beispielsweise können Sie von Ihrem eigenen Shell-Programm die vollständige Befehlszeile in eine Umgebungsvariable schreiben lassen (stellen Sie sich
MY_COMMAND_LINE
zum Beispiel vor) - oder Sie verwenden eine andere Art der Kommunikation zwischen Prozessen, um die Befehlszeile von der Shell zum untergeordneten Prozess zu übertragen -.Ich verstehe nicht, warum Sie das tun möchten, aber Sie könnten eine Shell programmieren, die sich so verhält (aber ich empfehle, dies nicht zu tun).
BTW, könnte ein Programm durch ein Programm gestartet , das ist nicht eine Schale ( die aber tut Gabel (2) dann execve (2) , oder ein nur
execve
ein Programm in seinem aktuellen Prozess zu starten). In diesem Fall gibt es überhaupt keine Befehlszeile und Ihr Programm könnte ohne einen Befehl gestartet werden ...Beachten Sie, dass Sie möglicherweise ein (spezialisiertes) Linux-System ohne installierte Shell haben. Das ist seltsam und ungewöhnlich, aber möglich. Sie müssen dann ein spezielles Init- Programm schreiben, das nach Bedarf andere Programme startet - ohne Verwendung einer Shell, sondern durch Ausführen von
fork
&execve
Systemaufrufen.Lesen Sie auch Betriebssysteme: Drei einfache Teile und vergessen Sie nicht, dass dies
execve
praktisch immer ein Systemaufruf ist (unter Linux werden sie in syscalls (2) aufgelistet , siehe auch intro (2) ), die den virtuellen Adressraum (und einige andere ) neu initialisieren Dinge) des Prozesses , der es tut.quelle
argv[0]
der Programmname und die übrigen Elemente für die Argumente POSIX-Spezifikationen sind und nicht geändert werden können. Eine Laufzeitumgebung könnteargv[-1]
für die Kommandozeile angegeben werden, nehme ich an ...execve
Dokumentation genauer durch . Sie können nicht verwendenargv[-1]
, es ist undefiniertes Verhalten, um es zu verwenden.execvepluscmd
Funktion mit einem zusätzlichen Parameter (oder einer Argv-Konvention) aufrufen , der Syscall erstellt einen Argumentvektor für main, der einen Zeiger auf die Befehlszeile vor dem Zeiger auf den Programmnamen enthält, und übergibt dann die Adresse des Zeigers auf den Programmnamen wieargv
beim Aufrufen des Programmsmain
...sh
. Ist also nicht neu.Sie können Ihrer Shell jederzeit mitteilen, welcher Shell-Code zu ihrer Ausführung geführt hat. Zum Beispiel mit
zsh
, indem Sie diese Informationen in der$SHELL_CODE
Umgebungsvariablen mit dempreexec()
Hook übergeben (printenv
als Beispiel, würden Siegetenv("SHELL_CODE")
in Ihrem Programm verwenden):Alle diese würden ausführen
printenv
als:Ermöglicht
printenv
das Abrufen des zsh-Codes, der zur Ausführungprintenv
dieser Argumente führt. Was Sie mit diesen Informationen anfangen möchten, ist mir nicht klar.Mit
bash
dem Merkmal am nächstenzsh
istpreexec()
mit seiner würden$BASH_COMMAND
in einerDEBUG
Falle, aber beachten Sie, dass einbash
gewisses Maß tut der in das Umschreiben (und insbesondere refactors einige der Leerzeichen als Trennzeichen verwendet wird ) , und das ist auf jeden angewandt (na ja, etwas) Befehl Führen Sie nicht die gesamte Befehlszeile aus, wie an der Eingabeaufforderung angegeben (siehe auch diefunctrace
Option).Sehen Sie, wie einige der Leerzeichen, die Trennzeichen in der Shell-Sprachensyntax sind, in 1 gequetscht wurden und wie nicht immer die vollständige Befehlszeile an den Befehl übergeben wird. Also in deinem Fall wohl nicht sinnvoll.
Beachten Sie, dass ich Ihnen davon abraten würde, da Sie potenziell vertrauliche Informationen an jeden Befehl weitergeben, wie in:
würde dieses Geheimnis an beide
wc
und weitergebenuntrustedcmd
.Natürlich können Sie so etwas auch für andere Sprachen als die Shell tun. In C könnten Sie beispielsweise einige Makros verwenden, die den C-Code, der einen Befehl ausführt, in die Umgebung exportieren:
Beispiel:
Sehen Sie, wie einige Leerzeichen vom C-Preprozessor komprimiert wurden, wie im Bash-Fall. In den meisten, wenn nicht allen Sprachen, spielt der in Begrenzungszeichen verwendete Speicherplatz keine Rolle. Daher ist es nicht verwunderlich, dass der Compiler / Interpreter hier etwas Freiheit mit sich nimmt.
quelle
BASH_COMMAND
enthielt es nicht die ursprünglichen Leerzeichen, die die Argumente trennten, so dass dies für die wörtliche Anforderung des OP nicht verwendbar war. Enthält diese Antwort eine Demonstration für diesen speziellen Anwendungsfall?Ich werde nur hinzufügen, was in den anderen Antworten fehlt.
Nein
Siehe andere Antworten
Vielleicht irgendwie
Es gibt nichts, was im Programm getan werden kann, aber es gibt etwas, was in der Shell getan werden kann, wenn Sie das Programm ausführen.
Sie müssen Anführungszeichen verwenden. Also statt
Sie müssen eine davon tun
Dies übergibt ein einzelnes Argument mit allen Leerzeichen an das Programm. Es gibt einen Unterschied zwischen den beiden, die zweite ist wörtlich, genau die Zeichenfolge, wie sie angezeigt wird (mit der Ausnahme, dass
'
als eingegeben werden muss\'
). Der erste interpretiert einige Zeichen, ist jedoch in mehrere Argumente unterteilt. Weitere Informationen finden Sie unter Shell-Anführungszeichen. Die Shell muss also nicht neu geschrieben werden, darüber haben die Shell-Designer bereits nachgedacht. Da es sich jetzt jedoch um ein Argument handelt, müssen Sie mehr innerhalb des Programms übergeben.Option 2
Gib die Daten über stdin weiter. Dies ist der normale Weg, um große Datenmengen in einen Befehl zu bekommen. z.B
oder
./myprog
Tell me what you want to tell me:
aaaa bbb
ctrl-d
(Kursivschrift wird vom Programm ausgegeben)
quelle
./myprog␣"␣␣␣␣␣aaa␣␣␣␣␣␣bbb"
ausführt ( im allgemeinen in einem untergeordneten Prozess) die Datei gespeichert in./myprog
und übergibt sie zwei Argumente:./myprog
und␣␣␣␣␣aaa␣␣␣␣␣␣bbb
(argv[0]
undargc[1]
,argc
wobei 2) und wie in den OPs, der Raum, der die beiden Argumente trennt , ist nicht in irgendeiner Weise weitergegeben zumyprog
.