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 echoBinä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 echoBinä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 coreutilsund nicht printfsofort 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 printfImplementierungen unterstützen leicht unterschiedliche Formatierungen (einschließlich moderner printfImplementierungen, z . B. basheingebaute vs busybox printf).
Wenn Sie möchten, dass die zusätzliche Zeilenumbruch an Ihre Ausgabe angehängt wird, \nist 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 printfoder die Sie ausgeben Zeilenumbruchzeichen für sich mit einem bloßen echoohne 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 printfich 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 echoextern 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 exprist, dass es Teilzeichenfolgen aus einem Argument mit einer Regex-Übereinstimmung extrahieren und drucken kann. Dies ist im Grunde eine portablere Version des ursprünglichen echoVerhaltens (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 XVorderseite 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 exprBefehl als falsch interpretiert wird exprSchlüsselwort, und letzteres stellt dann sicher, dass das X nicht tatsächlich gedruckt wird.
awk
Hier ist ein kompakter awkEinzeiler, 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 awkImplementierungen in Ihren Skripten einfacher / einfacher / sauberer als das echoArbeiten für Sie, da es zwar definitiv viele Abweichungen zwischen awks gibt, es jedoch weniger Variationen gibt, auf die getestet werden muss, als echowenn 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. echoFü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 awkBefehl gedruckt wird. Ich empfehle, den :Befehl no-op links in der Pipe durch echoArgumente ohne Argumente zu ersetzen. Aber ich habe diese Idee nicht sorgfältig auf ihre allgemeine Portabilität geprüft.
echodurchgeleitet sedoder ähnlich
Wenn Sie wissen, dass Ihre Eingaben nichts Besonderes sind (es werden keine Oktal-Escapezeichen mit Backslashes wie \000in 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 echoArbeiten ausführen Wenn Sie etwas anderes haben, können Sie die echoAusgabe vorverarbeiten :
echo X-e | sed '1 s/^X//'
Bei begrenzten, genau definierten Eingaben können Sie möglicherweise mit einfachen sedErsetzungen 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 echogenau 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 echoirgendwo 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 echoShell-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 \055oder -), 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 -nlohnt 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 -nund die leere Zeichenfolge.
Vielleicht möchten Sie auch konsultieren autoconfund m4Quellen, weil ich denke, dass diese Tools sich sehr bemühen, ein Echo zu finden, mit dem sie eindeutig drucken können, wenn sie kein printfoder 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 echoBeste ist. Es ist sehr wahrscheinlich, dass bestimmte echonicht 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, echoum 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 --helpden 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 PS1in Shells \$\anstelle von \w \$). Ich persönlich habe nicht genug Gründe, dies zu tun, echoweil ich auf Systemen, die ich tatsächlich verwende, echobei fast allen ernsthaften Arbeiten einfach ignoriere , aber jemand anderes hat möglicherweise ein ebenso starkes Gefühl für das Standardverhalten echowie 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 echoinstalliert sind gecho, sodass diese Systeme weder durch eine effiziente Suche nach den PATHund wahrscheinlich installierten Speicherorten noch durch eine Bruteforce-Suche nach nur genannten Dateien abgefangen echowerden.
Und ich würde tatsächlich wetten, dass auf mehr Systemen eine Skriptsprache wie perlinstalliert installiert ist, die das kann, was Sie wollen, als auf Systemen, die coreutils echospeziell über GNU verfügen : Einige Skriptsprachen sind allgegenwärtig und haben meistens eine Implementierung oder eine genau definierte Spezifikation, während echoImplementierungen dies sind unzählige und folgen Sie genau einer Spezifikation: "Machen Sie etwas anderes als so viele andere echoImplementierungen wie möglich".
printf).