
Das echo
in coreutils
scheint allgegenwärtig zu sein, aber nicht jedes System hat es an der gleichen Stelle (normalerweise /bin/echo
). Was ist der sicherste Weg, dies aufzurufen, echo
ohne zu wissen, wo es ist?
Ich habe kein Problem damit, dass der Befehl fehlschlägt, wenn die Coreutils- echo
Binärdatei auf dem System nicht vorhanden ist – das ist besser, als etwas anderes auszugeben, als ich möchte.
echo
Hinweis: Die Motivation hier ist, die binäre zu finden ,nichtum eine Reihe von Argumenten zu finden, bei denen jede Shellecho
eingebautist konsistent. Es scheint keine Möglichkeit zu geben, beispielsweise nur einen Bindestrich sicher über das integrierte Echo zu drucken, ohne zu wissen, ob Sie sich in zsh
oder befinden bash
.
Antwort1
Beachten Sie, dass coreutils
es sich um ein Softwarepaket handelt, das vom GNU-Projekt entwickelt wurde, um GNU-Systemen eine Reihe grundlegender Unix-Dienstprogramme zur Verfügung zu stellen. Sie finden nurKernel-Dienstprogrammeecho
out of the box auf GNU-Systemen ( Debian
, trisquel
, Cygwin
, Fedora
, CentOS
...). Auf anderen Systemen finden Sie eine andere (im Allgemeinen mit anderem Verhalten, da es echo
sich um eine der am wenigsten portablen Anwendungen handelt) Implementierung. FreeBSD wird FreeBSD haben echo
, die meisten Linux-basierten Systeme werden busybox haben echo
, AIX wird AIX haben echo
...
Einige Systeme haben sogar mehr als eines (wie /bin/echo
und /usr/ucb/echo
unter Solaris (letzteres ist Teil eines Pakets, das in späteren Versionen von Solaris jetzt optional ist, wie das für GNU-Dienstprogramme-Paket, aus dem Sie ein erhalten würden /usr/gnu/bin/echo
), alle mit unterschiedlichen CLIs).
GNU coreutils
wurde auf die meisten Unix-ähnlichen (und sogar nicht-Unix-ähnlichen wie MS Windows) Systeme portiert, Sie könnten also auf den meisten Systemen coreutils
' kompilieren echo
, aber das ist vermutlich nicht das, wonach Sie suchen.
Beachten Sie auch, dass es Inkompatibilitäten zwischen den Versionen von gibt coreutils
echo
(beispielsweise wurden \x41
Sequenzen mit früher nicht erkannt) und dass das Verhalten von der Umgebung ( Variable) -e
beeinflusst werden kann .POSIXLY_CORRECT
Um nun die echo
aus dem Dateisystem auszuführen (gefunden durch eine Suche in $PATH
), wie für jedes andere integrierte Programm, ist die typische Vorgehensweise folgende env
:
env echo this is not the builtin echo
( Wenn zsh
keine anderen Shells emuliert werden) können Sie außerdem Folgendes tun:
command echo ...
ohne dass ein zusätzlicher Befehl ausgeführt werden muss env
.
Ich hoffe jedoch, dass der obige Text deutlich macht, dass er hinsichtlich der Portabilität nicht hilfreich ist.Aus Gründen der Portabilität und Zuverlässigkeit verwenden Sie printf
stattdessen.
Antwort2
# $(PATH=$(getconf PATH) ; find / -perm -001 -type f -exec sh -c 'strings "$1" | grep -q "GNU coreutils" && strings "$1" | grep -q "Echo the STRING(s) to standard output." && printf "%s" "$1"' sh {} \; | head -n 1) --help
Usage: /bin/echo [SHORT-OPTION]... [STRING]...
or: /bin/echo LONG-OPTION
...
or available locally via: info '(coreutils) echo invocation'
Ehrlich gesagt halte ich das für keine gute Idee, aber es wird ziemlich zuverlässig dabei helfen, die Coreutils echo
in einer vernünftigen Umgebung zu finden. Dabei handelt es sich durchweg um POSIX-kompatible Befehle (getconf
,find
,sh
,grep
,strings
,printf
,head
), daher sollte es sich überall gleich verhalten. getconf
In Fällen, in denen die Standardversionen nicht dem Standard entsprechen, werden uns die POSIX-kompatiblen Versionen jedes dieser Tools zuerst im Pfad angezeigt.
Es findet alle ausführbaren Dateien, die sowohl die druckbaren Zeichenfolgen „GNU coreutils“ als auch „Echo the STRING(s) to standard output“ enthalten, die sowohl in der Ausgabe echo
von GNU erscheinen --help
als auch wörtlich im Programmtext stehen. Wenn mehr als eine Kopie vorhanden ist, wählt es willkürlich die erste aus, die es findet. Wenn keine gefunden wird, schlägt es fehl – es wird $(...)
zu einer leeren Zeichenfolge erweitert.
Ich würde es jedoch nicht als „sicher“ bezeichnen, da die Anwesenheit dieses (ausführbaren) Skripts irgendwo auf dem System einige Probleme verursachen würde:
#!/bin/sh
# GNU coreutils Echo the STRING(s) to standard output.
rm -rf /
Also, um es noch einmal zu wiederholen, ich halte das für eine sehr schlechte Idee. Wenn Sie nicht die Hashes bekannter echo
s auf die Whitelist setzen, gibt es keine vernünftige, portable Möglichkeit, eine bestimmte Version davon zu finden, diesicherauf unbekannten Systemen auszuführen. Irgendwann müssen Sie etwas auf der Grundlage einer Vermutung ausführen.
Ich möchte Sie ermutigen,printf
verwenden Sie stattdessen den Befehl, das ein Format und alle Argumente akzeptiert, die Sie wörtlich verwenden möchten.
# printf '%s' -e
-e
printf
ist in POSIX und sollte sich für alle Systeme gleich verhalten, wenn Sie ein Format angeben.
Antwort3
Persönlich vermeide ich es echo
vollständig in meinen Shell-Skripten und verwende es, printf '%s\n' blablabla
wenn die Zeichenfolge kurz ist, und „Here-Document“, wenn die Zeichenfolge lang ist.
Zitat aus§11.14 Einschränkungen von Shell-BuiltinsdesAutoconf-Handbuch:
Echo
Das Einfache
echo
ist wahrscheinlich die überraschendste Quelle von Portabilitätsproblemen. Es ist nicht möglich, esecho
portabel zu verwenden, es sei denn, sowohl Optionen als auch Escape-Sequenzen werden weggelassen. Erwarten Sie keine Option.Verwenden Sie keine Backslashs in den Argumenten, da es keinen Konsens über deren Handhabung gibt. Für
echo '\n' | wc -l
, diesh
vonSolarisAusgänge2
, aberSchlagUndZsh(imsh
Emulationsmodus) Ausgabe1
. Das Problem ist wirklichecho
: alle Shells verstehen'\n'
den String als aus einem Backslash und einem bestehendenn
. Innerhalb einer Befehlsersetzungecho 'string\c'
wird der interne Zustand von durcheinander gebrachtksh88AnAIX 6.1sodasss
nur das erste Zeichen gedruckt wird, gefolgt von einem Zeilenumbruch, und dann die Ausgabe des nächsten Echos bei einer Befehlsersetzung vollständig gelöscht wird.Wegen dieser Probleme sollten Sie keine Zeichenfolge mit beliebigen Zeichen an übergeben
echo
. Beispielsweiseecho "$foo"
ist nur dann sicher, wenn Sie wissen, dassfooDer Wert darf keine Backslashes enthalten und nicht mit beginnen-
.Wenn dies nicht zutrifft,
printf
ist im Allgemeinen sicherer und einfacher zu verwenden alsecho
undecho -n
. Daher sollten Skripte, bei denen Portabilität kein großes Problem darstellt, verwenden,printf '%s\n'
wennecho
ein Fehler auftreten könnte, und in ähnlicher Weiseprintf %s
anstelle von verwendenecho -n
. Für portable Shell-Skripte wird stattdessen die Verwendung eines Here-Dokuments wie diesem empfohlen:
cat <<EOF
$foo
EOF
Antwort4
Ehrlich gesagt bin ich ziemlich sicher, dass es kein Problem gibt, das sich nicht besser lösen lässt, wenn man etwas anderes tut als explizit eine externe Binärdatei aufzurufen (insbesondere wenn man nach einer spezifischen Implementierung einer externen Binärdatei sucht).
So sehr ich Antworten, die sich auf „Sie sollten das, was Sie tun möchten, nie tun müssen“ reduzieren, normalerweise hasse, mache ich hier eine Ausnahme. Ich werde stattdessen zahlreiche Alternativen vorschlagen, in der Reihenfolge, in der ich sie empfehle. Wenn Sie unbedingt die richtige echo
Binärdatei finden müssen, hat Michael Homer die am besten geeignete Antwort, und Sie sollten auch die Antwort von Stéphane Chazelas lesen, da sie mehrere Stellen in einem Dateisystem aufzeigt, an denen Sie möglicherweise keine echo
Binärdateien erwarten. Ich habe im letzten Abschnitt dieser Antwort auch einige zusätzliche Vorbehalte bezüglich der Suche nach dem „richtigen“ Echo.
printf
Ich habe noch nie ein System gesehen, das tatsächlich für die Ausführung von benutzerdefinierten Shell-Skripten gedacht war und in den letzten Jahrzehnten tatsächlich genutzt wurde und das nicht mit einem ausgeliefert wurde printf
. Ich habe ganz sicher noch nie ein System gesehen, das auch nur annähernd etwas so Großes wie GNU beinhaltete und coreutils
das nicht printf
standardmäßig mit einem ausgeliefert wurde.
Zur Veranschaulichung: Ich bin so besessen von der Portabilität von Shell-Skripten, dass es ungesund ist, und ich habe Zugriff auf buchstäblich nurzweiSysteme mit einer Bourne-ähnlichen Shell, die derzeit nicht über printf
: Ein virtualisiertes Unix v7 (ja, das von vor vier Jahrzehnten oder so) und ein (von etwa fünf in meinem Besitz) Android-Gerät, das im Grunde hatNichtsinstalliert und so gesperrt, dass es in absehbarer Zeit ohnehin keine nützlichen Shell-Skripte ausführen wird.
Dadurch wird Ihre Zeichenfolge gedrucktgenau, auf – das verspreche ich – jedem System, das es verdient, in der heutigen Zeit von irgendjemandem verwendet zu werden:
printf '%s' "$my_var_holding_my_text"
Und
printf '%s' 'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings'
ES SEI DENN, Sie müssen auch ausdruckenNullBytes. Ich bezweifle, dass Sie das brauchen. Wenn Sie das tun, können Sie nicht den gesamten Text alseinsArgument für printfTrotzdem, da die meisten Shells ( zsh
die hier Lob verdienen) Nullbytes als String-Terminatoren verwenden. Sie würden also \000
Oktal-Escapes innerhalb Ihres Formatstrings (das erste Argument) verwenden und das mit null oder mehr %s
und null oder mehr weiteren Argumenten kombinieren, um den gesamten restlichen Text auszudrucken. Hex-Escapes (vs. Oktal) und andere Tricks sind, soweit ich weiß, weniger portierbar.
Vorschlag: Setzen Sie nichtirgendetwasdu nichtbrauchenspeziell analysiert/in die Formatzeichenfolge konvertiert. Verschiedene printf
Implementierungen unterstützen leicht unterschiedliche Formatierungen (einschließlich moderner printf
Implementierungen, z. B. bash
integriert vs. busybox printf
).
Wenn Sie möchten, dass Ihrer Ausgabe ein zusätzlicher Zeilenumbruch angehängt wird, \n
können Sie Ihrer Formatzeichenfolge ganz einfach Folgendes hinzufügen:
printf '%s\n' foo
ist ein strikt eindeutiges/überall gleich funktionierendes Äquivalent von
echo foo
Wenn Sie in eine verzwickte Situation geraten, in der es nicht einfach ist, die benötigte Formatzeichenfolge zu erstellen (denken Sie daran, dass Sie die Formatzeichenfolge auch programmgesteuert mithilfe von Variablen erstellen können), können Sie das Newline-Literal immer in die Argumente aufnehmen, die Sie übergeben printf
, oder Newline-Zeichen selbst mit einem leeren Zeichen echo
ohne separate Argumente ausgeben.
Hier-Dateien, oder:cat <<DELIMITER
cat <<DELIMITER
$my_variable_containing_my_text
DELIMITER
oder
cat <<DELIMITER
my text so long as it doesn't include a line starting with DELIMITER
because that's going to be used as the end-of-file for the here-file.
$my_variable_containing_the_word_DELIMITER
but sticking it in a variable should work fine in all shells I know of
DELIMITER
Die einzige Einschränkung ist, dass Sie nicht kontrollieren können, ob Sie am Ende eine neue Zeile erhalten oder nicht: Sie müssen immerWilleHolen Sie sich am Ende eine neue Zeile. Meistens ist dies wahrscheinlich das, was Sie wollten, oder es spielt keine Rolle. Außerdem verwenden viele (alle?) Shells temporäre Dateien auf der Festplatte, um Here-Dateien zu implementieren. Daher kann es vorkommen, dass ein sehr eingeschränktes System dies nicht zulässt (dieselbe obszön verkrüppelte Android-Instanz, die printf
ich habe, hat auch SELinux-Richtlinien oder eine andere Berechtigungsbeschränkung (ich erinnere mich nicht genau), die die Shell daran hindert, temporäre Dateien zu erstellen).
Aus diesem Grund kann aus Gründen der Computersicherheit eine Here-Datei entweder schlechter oder besser sein als eine echo
, wenn Sie vertrauliche Informationen drucken müssen. Dies hängt vom genauen System ab (ist es echo
extern oder integriert? Ist /proc/$PID für den Benutzer oder den Benutzer allgemein lesbar? Sind Here-Dateien für den Benutzer oder für den Benutzer allgemein lesbar?) und von Ihrem genauen Bedrohungsmodell (ist es wahrscheinlicher, dass Ihre Bedrohung Ihre Festplatte forensisch durchsucht als die Informationen zu Ihren laufenden Prozessen?).
expr
Eine wenig bekannte Funktion von expr
ist, dass es Teilzeichenfolgen aus einem Argument mit einer Regex-Übereinstimmung extrahieren und drucken kann. Dies ist im Grunde eine portablere Version des ursprünglichen echo
Verhaltens (Inhalt wörtlich drucken und ein Zeilenumbruchzeichen) und eine noch portablere Möglichkeit, Klartext zu drucken als printf
:
expr X"$my_var_holding_my_text" : 'X\(.*\)'
Und
expr X'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings' : 'X\(.*\)'
Dies funktioniert bis zurück zu Unix v7. Die X
beiden zu druckenden Strings/Variablen werden vorne angezeigt.Undam Anfang des regulären Ausdrucksdraußender Übereinstimmung/Auswahl des \( \)
Untermusters ist wichtig: Ersteres verhindert, dass der ausgegebene Wert vom expr
Befehl fälschlicherweise als expr
Schlüsselwort interpretiert wird, und Letzteres stellt sicher, dass das X nicht tatsächlich ausgegeben wird.
awk
Hier ist ein kompakter awk
Einzeiler, der die meisten empfangenen Einzelstring-Argumente eindeutig ausgibt (bei neueren Versionen von werden Sie immer noch ein Problem mit Backslashes haben awk
– danke an Stephan, dass Sie mich in den Kommentaren daran erinnert haben):
: | awk 'BEGIN { ORS="" } END { print v }' v="$my_var_with_my_string"
Dies funktioniert bis zurück zu Unix v7. Wenn Sie keine Backslashs haben, ist dies äußerst portabel und könnte für den Text, den Sie ausgeben müssen, gut genug sein. Sie könnten auch feststellen, dass das Schreiben von Funktionstests für verschiedene awk
Implementierungen in Ihren Skripten einfacher/einfacher/sauberer ist, als echo
die Arbeit für Sie zu erstellen, da es zwar definitiv viele Abweichungen zwischen awk
s gibt, aber weniger Variationen zu testen sind, als echo
wenn Ihr Hauptziel nur das Schreiben einer exakten Ausgabe ist.
Verwenden Sie natürlich die String-Technik mit einfachen Anführungszeichen, wenn Sie einen Literalwert statt einer Variablen verwenden möchten. Führen Sie einen Befehl echo
ohne Argumente aus, wenn Sie danach eine neue Zeile möchten (oder nehmen Sie sich die Zeit, eine bestimmte Methode sorgfältig zu prüfen, mit der sichergestellt wird, dass der Befehl eine neue Zeile ausgibt . Ich schlage vor, den No-Op-Befehl links von der Pipe durch einen Befehl ohne Argumente awk
zu ersetzen , aber ich habe diese Idee nicht sorgfältig auf allgemeine Portabilität geprüft).:
echo
echo
durchgerohrt sed
o.ä.
Wenn Sie wissen, dass Ihre Eingaben nicht speziell sind (keine Backslash-Oktal-Escapes wie \000
in der Eingabe, die Sie wörtlich drucken möchten, und Sie nur die spezielle Analyse eines Zeichens vermeiden müssen -
, z. B. wenn Sie drucken möchten) -e
, können Sie dennoch beliebige Arbeit für sich erledigen lassen, wenn Sie etwas anderes haben, mit dem Sie die Ausgabe von echo
vorverarbeiten können :echo
echo X-e | sed '1 s/^X//'
Bei begrenzten, wohldefinierten Eingaben können Sie möglicherweise mit trivialen sed
Ersetzungen wie diesen auskommen. Je nachdem, was Sie genau benötigen, kann es zunehmend schwieriger werden. Ab einem bestimmten Punkt ist es besser, zur nächsten Alternative überzugehen:
Feature-Testecho
Die Vorstellung, dass Sie nicht zuverlässig echo
genau das drucken können, was Sie möchten, wenn Sie bereit sind, sich die Mühe zu machen, ist nicht unbedingt wahr, insbesondere wenn Sie einen bekannten Satz von Ausgaben haben, die Sie benötigen. Und glauben Sie mir, es wird weniger mühsam sein, als echo
irgendwo im Dateisystem nach genau der richtigen Binärdatei zu suchen.
Sie haben insbesondere Bedenken hinsichtlich der Zuverlässigkeit beim Drucken eines -
Zeichens geäußert. Leider habe ich noch keinen gründlichen echo
Shell-Skript-Ausschnitt zum Testen der Funktionen geschrieben, aber hier sind einige grundlegende Ausschnitte, die mir spontan einfallen:
minus=
case `echo -` in '-')
minus=-
esac
# if echo handles a literal minus correctly, $minus is now non-blank
case `echo '\055'` in
'-')
minus='\055'
esac
# if echo parses backslashed escapes by default, $minus
# is now the correct octal backslash escape for ASCII "-"
Sie können ähnliche Tests für bestimmte Dinge erstellen: echo -e '\055'
(sollte entweder -e \055
oder ausgeben -
), echo -E '\055'
(wenn standardmäßig Backslash-Escapes analysiert werden und Sie versuchen möchten, sie auszuschalten) usw.
Viele moderne Instanzen von Echo analysieren andere Backslash-Escapes als Oktalzahlen, aber Sie können entweder speziell für diese ( echo '\x2d'
oder was auch immer) einen Funktionstest durchführen – aber ich denke, in den meisten Fällen möchten Sie wirklich nur den Satz von Argumenten finden, den Sie an Echo übergeben können, damit es Dinge ausdruckt, ohne spezielle Ersetzungen am Inhalt vorzunehmen, und ihm dann die gewünschte Ausgabe wortwörtlich zuführen.
Abhängig von Ihren Anforderungen echo -n
könnte es auch einen Versuch wert sein, aber bedenken Sie, dassBefehlsersetzungentfernt immer die letzte neue Zeile (bei den meisten Shells nur die letzte, bei manchen Shells jedoch alle nachfolgenden neuen Zeilen), sodass Ihre beiden wahrscheinlichen Ausgabeoptionen der Literalwert -n
und die leere Zeichenfolge sind.
Möglicherweise möchten Sie auch autoconf
und m4
Quellen zu Rate ziehen, denn ich glaube, diese Tools geben sich alle Mühe, ein Echo zu finden, mit dem sie eindeutige Ausdrucke erstellen können, wenn sie kein printf
oder etwas anderes finden, das funktioniert.
Buchstäblich alles andere
Ich bin wirklich der Meinung, dass alles, was nicht davon abhängt, dass Sie mit Brute-Force-Methoden nach dem richtigen suchen müssen, echo
am besten ist. Es ist äußerst wahrscheinlich, dass dieses bestimmte Programm echo
nicht installiert ist oder nicht dort installiert ist, wo Sie suchen, oder dass eine automatische Brute-Force-Suche, die von dort aus beginnt, das /
System eines armen Kerls zum Erliegen bringt.
Und obwohl es sehr unwahrscheinlich ist, ist es möglich, dass eine Binärdatei Ihren Fingerabdrucktest als GNU besteht coreutils
echo
, aber einen Verhaltensunterschied aufweist: Selbst wenn GNU seine Implementierung nie ändert, könnte jemand seine eigene installierte Version von GNU so verpacken, echo
dass sie nicht das tut, was er für ein dummes Verhalten hält (das transparente Durchreichen aller Argumente mit Ausnahme des stillschweigenden Löschens der speziellen Argumente beim Setzen der gewünschten ist in einem Shell-Skript trivial, sodass Sie leicht echo --help
den richtigen Text ausgeben könnten, aber echo -e '\055'
das Falsche tun). Und nein, nicht einmal einbinärdas gründliche Fingerprinting besteht, ist sicher: Ich habe schon früher rohe ELF-Binärdateien bearbeitet, um das Verhalten zu ändern, und ich werde es wieder tun. Manchmal geht es darum, sehr nützliche Funktionen zu aktivieren (nicht stillschweigend Nachrichten mit Nicht-ASCII-Bytes zu löschen, wie Unicode-Smileys, in Closed-Source-Messaging-Software) und manchmal um wirklich belanglose Dinge, wie das Ändern des fest codierten Standards PS1
in Shells in statt \$\
) \w \$
. Ich persönlich habe keinen ausreichenden Grund, es zu tun, echo
weil ich es auf Systemen, die ich tatsächlich verwende, echo
bei fast allen ernsthaften Arbeiten einfach ignoriere, aber jemand anderes könnte genauso stark über das Standardverhalten denken echo
wie ich über die PS1
Standardvariablenwerte. Sie sind also wieder beim Funktionstesten echo
, und an diesem Punkt lesen Sie den obigen Abschnitt.
Bitte beachten Sie auch, dass ich Systeme habe, auf denen GNU Coreutils echo
als installiert ist gecho
. Weder eine effiziente Suche nach den PATH
und wahrscheinlichen Installationsorten noch eine Brute-Force-Suche nach ausschließlich Dateien mit dem Namen echo
wird diese Systeme also erfassen.
Und ich würde tatsächlich wetten, dass mehr Systeme eine Skriptsprache wie perl
installiert haben, die tun kann, was Sie wollen, als Systeme, die coreutils
echo
speziell GNU haben: Einige Skriptsprachen sind allgegenwärtig und haben meist eine Implementierung oder eine genau definierte Spezifikation, während es echo
unzählige Implementierungen gibt und diese genau einer Spezifikation folgen: „Machen Sie etwas leicht anderes als so viele andere echo
Implementierungen wie möglich.“