Ehrlich gesagt bin ich ziemlich zuversichtlich, dass es kein Problem gibt, das nicht besser gelöst werden kann, wenn man etwas anderes tut, als explizit eine externe Binärdatei aufzurufen (insbesondere nach einer bestimmten Implementierung einer externen Binärdatei zu suchen).
So sehr ich normalerweise Antworten hasse, die sich auf "Sie sollten niemals das tun müssen, was Sie tun möchten" beschränken, mache ich hier eine Ausnahme. Ich werde stattdessen zahlreiche Alternativen vorschlagen, in der Reihenfolge, wie stark ich sie vorschlage. 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 dadurch mehrere Speicherorte in einem Dateisystem angezeigt werden, von denen Sie möglicherweise nicht erwarten, dass sie echo
Binärdateien finden . Ich habe auch einige zusätzliche Einschränkungen bezüglich der Suche nach dem "richtigen" Echo in meinem letzten Abschnitt dieser Antwort.
printf
Ich habe noch nie ein System gesehen, das eigentlich benutzerdefinierte Shell-Skripte ausführen sollte und das in den letzten Jahrzehnten eine echte Verwendung fand, das nicht mit einem ausgeliefert wurde printf
. Ich habe sicherlich noch nie ein System gesehen, das auch nur annähernd so groß wie GNU war coreutils
und nicht printf
sofort ausgeliefert wurde.
Aus Sicht der Perspektive bin ich so besessen davon, dass die Portabilität von Shell-Skripten ungesund ist, und ich habe derzeit Zugriff auf buchstäblich nur zwei Systeme mit einer Bourne-ähnlichen Shell, die keine haben printf
: Ein virtualisiertes Unix v7 (ja, das von vier vor Jahrzehnten oder so) und ein (von ungefähr fünf in meinem Besitz) Android-Gerät, auf dem im Grunde nichts installiert ist und das so gesperrt ist, dass es sowieso bald keine nützlichen Shell-Skripte mehr ausführen wird.
Dies wird Ihre Zeichenfolge genau auf - ich verspreche - jedes System drucken , 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'
AUSSER Sie müssen auch Null- Bytes ausdrucken . Ich bezweifle, dass du musst. Wenn Sie dies tun, können Sie ohnehin nicht den gesamten Text als ein Argument für printf verwenden , da die meisten Shells (hier verdient Lob) Null-Bytes als String-Terminatoren verwenden. Sie würden also oktale Escapezeichen in Ihrer Formatzeichenfolge (das erste Argument) verwenden und diese mit null oder mehr und null oder mehr weiteren Argumenten kombinieren, um den gesamten anderen Text zu drucken. Hex Escape (vs Octal) und andere Tricks sind meines Wissens weniger portabel.zsh
\000
%s
Vorschlag: Fügen Sie nichts, was Sie nicht benötigen, speziell analysiert / in die Formatzeichenfolge konvertiert ein. Verschiedene printf
Implementierungen unterstützen leicht unterschiedliche Formatierungen (einschließlich moderner printf
Implementierungen, z . B. bash
eingebaute vs busybox printf
).
Wenn Sie möchten, dass die zusätzliche Zeilenumbruch an Ihre Ausgabe angehängt wird, \n
ist es trivial, Ihrer Formatzeichenfolge eine zusätzliche hinzuzufügen:
printf '%s\n' foo
ist ein streng eindeutiges / funktioniert-das-überall-gleichwertiges Äquivalent von
echo foo
Wenn Sie in eine komplizierte 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 Zeilenumbruchliteral jederzeit in die Argumente aufnehmen, an die Sie übergeben printf
oder die Sie ausgeben Zeilenumbruchzeichen für sich mit einem bloßen echo
ohne Argumente separat.
Here-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 , wenn Sie eine neue Zeile am Ende bekommen oder nicht: Sie immer wird eine neue Zeile am Ende bekommen. 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, sodass es möglich ist, dass ein sehr eingeschränktes System dies nicht zulässt (dieselbe obszön verkrüppelte Android-Instanz, ohne die printf
ich auch SELinux habe Richtlinien oder andere Berechtigungsbeschränkungen (ich erinnere mich nicht genau), die verhindern, dass die Shell temporäre Dateien erstellt).
Aus diesem Grund kann eine Here-Datei in einem Computer-Sicherheitshinweis, wenn Sie vertrauliche Informationen drucken müssen echo
, je nach genauem System entweder schlechter oder besser sein als eine (ist echo
extern oder eine integrierte? Is / proc / $ PID) Welt oder Benutzer lesbar? Sind hier Dateien Benutzer oder Welt lesbar?) und Ihr genaues Bedrohungsmodell (ist es wahrscheinlicher, dass Ihre Bedrohung Ihre Festplatte forensisch durchsucht als Ihre laufenden Prozessinformationen?)
expr
Ein wenig bekanntes Merkmal 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 (wörtliche Druckinhalte und ein Zeilenumbruchzeichen) und eine noch portablere Methode zum Drucken von einfachem Text 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 zurück zu Unix v7. Die X
Vorderseite sowohl der zu druckenden Zeichenfolge / Variablen als auch die Vorderseite des regulären Ausdrucks außerhalb der \( \)
Untermusterübereinstimmung / -auswahl ist wichtig: Ersteres verhindert, dass der Wert, den Sie drucken, vom expr
Befehl als falsch interpretiert wird expr
Schlüsselwort, und letzteres stellt dann sicher, dass das X nicht tatsächlich gedruckt wird.
awk
Hier ist ein kompakter awk
Einzeiler, der die meisten einzeiligen Argumente druckt, die er eindeutig erhält (Sie haben immer noch ein Problem mit Backslashes in der neueren Version von 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 zurück zu Unix v7. Wenn Sie keine Backslashes haben, ist dies extrem portabel und möglicherweise gut genug für den Text, den Sie ausgeben müssen. Möglicherweise finden Sie auch das Schreiben von Feature-Tests für verschiedene awk
Implementierungen in Ihren Skripten einfacher / einfacher / sauberer als das echo
Arbeiten für Sie, da es zwar definitiv viele Abweichungen zwischen awk
s gibt, es jedoch weniger Variationen gibt, auf die getestet werden muss, als echo
wenn Ihr Kernziel nur das Schreiben einiger ist genaue Ausgabe.
Verwenden Sie natürlich die Zeichenfolgentechnik in einfachen Anführungszeichen, wenn Sie ein Literal anstelle einer Variablen verwenden möchten. echo
Führen Sie ein Argument ohne Argumente aus, wenn Sie danach eine neue Zeile wünschen (oder nehmen Sie sich die Zeit, um eine bestimmte Methode genau zu überprüfen, um sicherzustellen, dass eine neue Zeile vom awk
Befehl gedruckt wird. Ich empfehle, den :
Befehl no-op links in der Pipe durch echo
Argumente ohne Argumente zu ersetzen. Aber ich habe diese Idee nicht sorgfältig auf ihre allgemeine Portabilität geprüft.
echo
durchgeleitet sed
oder ähnlich
Wenn Sie wissen, dass Ihre Eingaben nichts Besonderes sind (es werden keine Oktal-Escapezeichen mit Backslashes wie \000
in der Eingabe ausgegeben, die Sie buchstäblich drucken möchten, und Sie müssen nur vermeiden, ein -
Zeichen speziell zu analysieren , z. B. das Sie drucken möchten -e
, können Sie dennoch beliebige echo
Arbeiten ausführen Wenn Sie etwas anderes haben, können Sie die echo
Ausgabe vorverarbeiten :
echo X-e | sed '1 s/^X//'
Bei begrenzten, genau definierten Eingaben können Sie möglicherweise mit einfachen sed
Ersetzungen wie diesen davonkommen . Je nachdem, was genau Sie benötigen, wird es möglicherweise zunehmend schwieriger. Ab einem bestimmten Punkt ist es besser, mit der nächsten Alternative fortzufahren:
Funktionstest echo
Die Idee, dass Sie nicht zuverlässig echo
genau das drucken können, was Sie wollen, wenn Sie bereit sind, sich Mühe zu geben, um dies zu tun, ist nicht unbedingt wahr, insbesondere wenn Sie über eine bekannte Reihe von Ausgaben verfügen, die Sie benötigen. Und glauben Sie mir, es wird weniger schmerzhaft sein, als echo
irgendwo im Dateisystem nach der richtigen Binärdatei zu suchen .
Sie haben insbesondere Bedenken geäußert, ein -
Zeichen zuverlässig zu drucken . Leider habe ich noch kein gründliches echo
Shell-Skript-Snippet zum Testen von Funktionen geschrieben, aber hier sind einige grundlegende Snippets, die mir auf den Kopf gefallen sind:
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 echo -e '\055'
erstellen : (sollte entweder ausgegeben werden -e \055
oder -
), echo -E '\055'
(wenn Backslash standardmäßig analysiert wird und Sie versuchen möchten, sie auszuschalten) usw.
Viele moderne Echo-Instanzen analysieren andere Backslash-Escapezeichen als Oktalzahlen, aber Sie können entweder einen Feature-Test speziell für diese ( echo '\x2d'
oder was auch immer) durchführen - aber ich denke, in den meisten Fällen möchten Sie wirklich nur die Argumente finden, die Sie übergeben können Echo, damit es Dinge druckt, ohne spezielle Ersetzungen am Inhalt vorzunehmen, und dann die gewünschte Ausgabe wörtlich eingeben.
Abhängig von Ihren Anforderungen echo -n
lohnt es sich möglicherweise auch, einen Test durchzuführen. Beachten Sie jedoch, dass beim Ersetzen von Befehlen immer die letzte neue Zeile entfernt wird (nur die letzte bei den meisten Shells, aber alle nachfolgenden Zeilenumbrüche bei einigen Shells). Daher sind dies Ihre beiden wahrscheinlichen Ausgabeoptionen das Literal -n
und die leere Zeichenfolge.
Vielleicht möchten Sie auch konsultieren autoconf
und m4
Quellen, weil ich denke, dass diese Tools sich sehr bemühen, ein Echo zu finden, mit dem sie eindeutig drucken können, wenn sie kein printf
oder etwas anderes finden, das funktioniert.
Im wahrsten Sinne des Wortes alles andere
Ich denke wirklich, dass fast alles, was nicht davon abhängt, dass Sie brutal nach genau dem richtigen suchen müssen, das echo
Beste ist. Es ist sehr wahrscheinlich, dass bestimmte echo
nicht installiert werden oder nicht dort installiert werden, wo Sie suchen, oder dass eine automatische Bruteforce-Suche ab beginnt /
das System eines armen Kerls zum Crawlen bringt.
Und obwohl dies sehr unwahrscheinlich ist, ist es möglich, dass eine Binärdatei Ihren Fingerabdruck als GNU weitergibt coreutils
echo
, aber einen Verhaltensunterschied aufweist: Selbst wenn GNU ihre Implementierung nie ändert, kann jemand seine eigene installierte Version von GNUs einpacken, echo
um nicht das zu tun, was er in Betracht zieht Seien Sie ein dummes Verhalten (das transparente Durchlaufen aller Argumente, mit Ausnahme des stillschweigenden Löschens der speziellen Argumente beim Festlegen der gewünschten Argumente, ist in einem Shell-Skript trivial, sodass Sie leicht echo --help
den richtigen Text drucken können, echo -e '\055'
aber das Falsche tun). Und nein, nicht einmal eine BinärdateiDas gründliche Fingerabdruckverfahren ist sicher: Ich habe zuvor rohe ELF-Binärdateien bearbeitet, um das Verhalten zu ändern, und ich werde es erneut tun. Manchmal geht es darum, sehr nützliche Funktionen zu aktivieren (Nachrichten, die keine ASCII-Bytes enthalten, wie Unicode-Smileys, in Closed-Source-Messaging-Software stillschweigend zu löschen) und manchmal um wirklich kleine Dinge wie das Ändern der fest codierten Standardeinstellung PS1
in Shells \$\
anstelle von \w \$
). Ich persönlich habe nicht genug Gründe, dies zu tun, echo
weil ich auf Systemen, die ich tatsächlich verwende, echo
bei fast allen ernsthaften Arbeiten einfach ignoriere , aber jemand anderes hat möglicherweise ein ebenso starkes Gefühl für das Standardverhalten echo
wie für Standardvariablenwerte PS1
. Sie kehren also zum Testen von Funktionen zurück echo
. An diesem Punkt finden Sie den obigen Abschnitt.
Beachten Sie außerdem, dass ich Systeme habe, auf denen GNU-Coreutils echo
installiert sind gecho
, sodass diese Systeme weder durch eine effiziente Suche nach den PATH
und wahrscheinlich installierten Speicherorten noch durch eine Bruteforce-Suche nach nur genannten Dateien abgefangen echo
werden.
Und ich würde tatsächlich wetten, dass auf mehr Systemen eine Skriptsprache wie perl
installiert installiert ist, die das kann, was Sie wollen, als auf Systemen, die coreutils
echo
speziell über GNU verfügen : Einige Skriptsprachen sind allgegenwärtig und haben meistens eine Implementierung oder eine genau definierte Spezifikation, während echo
Implementierungen dies sind unzählige und folgen Sie genau einer Spezifikation: "Machen Sie etwas anderes als so viele andere echo
Implementierungen wie möglich".
printf
).