Der direkte Schuldige ist $phrase
in einfachen Anführungszeichen. Dies ist nicht das einzige Problem.
Was geschieht
Dies ist der relevante Code (Hinweis: Ich benutze Ellipsen …
für den am wenigsten interessanten Teil; diese Linie soll vom Menschen verstanden werden, nicht direkt in einer Shell ausgeführt):
find … -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "$phrase"' \;
Die Shell, die das Skript interpretiert, enthält den Wert der phrase
Variablen. Sagen wir mal der Wert ist volym
. Im obigen Befehl bleibt alles, was in einfachen Anführungszeichen steht, unberührt, denn so funktioniert das einfache Anführungszeichen. so $phrase
ist noch nicht ausgebaut. Die Shell analysiert nur \
die folgenden ;
Befehle und soll die Befehle nicht trennen. Sie sollte als Befehlszeilenargument für behandelt werden find
.
Wenn das find
Dienstprogramm ausgeführt wird, sieht es als Argumente (ab 0. Dh an sich find
selbst; ein Argument pro Zeile, mit Ausnahme …
mehrerer weniger interessanter Argumente):
find … -exec sh -c pdftotext "{}" - | grep --with-filename --label="{}" --color "$phrase" ;
Beachten Sie, dass die vorletzte Zeile ein langes Argument ist.
Nehmen wir an, es foo.pdf
wird gefunden und -exec
wird seine Arbeit machen. Alle Argumente zwischen -exec
und ;
werden zu einem neuen Befehl, {}
der durch ersetzt wird foo.pdf
. Der neue Befehl lautet (wieder beginnend mit dem 0. Argument; ein Argument pro Zeile):
sh -c pdftotext "foo.pdf" - | grep --with-filename --label="foo.pdf" --color "$phrase"
Läuft also sh
, er bekommt -c
und weiß daher, dass das nächste Argument so ausgeführt werden sollte, als wäre es in die Befehlszeile eingegeben worden:
pdftotext "foo.pdf" - | grep --with-filename --label="foo.pdf" --color "$phrase"
Dies ist der Moment $phrase
erweitert. Es erweitert sich zu nichts (das letzte Wort wird zu ""
), weil es nicht in dieser Shell gesetzt wurde. Es würde sich erweitern, volym
wenn Sie die Variable in Ihr Skript exportieren. aber du hast nicht Ich würde aber nicht exportieren; Meiner Meinung nach würde der Export die Umwelt unnötig belasten.
Lösung? Noch nicht
Das Anbringen $phrase
von einfachen Anführungszeichen scheint eine gute Idee zu sein. In einigen Fällen wird es funktionieren. Der naivste Ansatz:
find … -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "'$phrase'"' \;
Es ist fehlerhaft. Mit dem Satz sind " ; -exec rm "{}
dies Argumente, die wir find
sehen werden:
find … -exec sh -c pdftotext "{}" - | grep --with-filename --label="{}" --color "" ; -exec rm "{}" ;
Ihre PDFs sind weg. Künstliches Beispiel? Könnte sein. Selbst wenn Sie der Einzige sind, der das Skript verwendet, ist eine solche Sicherheitsanfälligkeit durch Code-Injektion nicht gut.
Dies war, weil überhaupt $phrase
nicht zitiert wurde. Sie wissen wahrscheinlich, dass Sie Variablen fast immer in Anführungszeichen setzen sollten. Lass uns das machen. Ein verbesserter Ansatz:
find … -exec sh -c 'pdftotext "{}" - | grep --with-filename --label="{}" --color "'"$phrase"'"' \;
Mit der Phrase wird " ; -exec rm "{}
dies find
sehen:
find … -exec sh -c pdftotext "{}" - | grep --with-filename --label="{}" --color "" ; -exec rm "{}" ;
Sieht etwas besser aus; immer noch fehlerhaft, weil foo.pdf
sh
will versuchen zu laufen:
pdftotext "foo.pdf" - | grep --with-filename --label="foo.pdf" --color "" ; -exec rm "foo.pdf"
Der letzte Teil wird höchstwahrscheinlich einen Fehler auslösen, da es keinen -exec
Befehl gibt. Was ist, wenn der Satz war " ; rm "{}
? Was passiert, wenn es war " ; rm -rf ~/"
.
Es gibt mehr. Lassen Sie die Phrase volym
(ziemlich sicher) sein, aber benennen Sie eine Ihrer PDF-Dateien "; rm -rf ~ #.pdf
(dies ist in wenigen Dateisystemen einschließlich der Ext-Familie möglich). Nachdem {}
-s ersetzt wurden, sh
wird Folgendes ausgeführt:
pdftotext "/home/ad0x/…/"; rm -rf ~ #.pdf" - | grep …
Ich denke, pdftotext
wird versagen (irrelevant); dann sind deine Dateien weg; dann #
beginnt ein Kommentar, was auch immer.
Lösung
Das ist der richtige Weg, um Ihre passieren {}
und $phrase
zu sh
sicher :
find … -exec sh -c 'pdftotext "$1" - | grep --with-filename --label="$1" --color "$2"' dummy {} "$phrase" \;
Wenn dies sh
der gegebenen Befehlsfolge ausführt, $1
erweitert wird, was auch immer find
für substituierte {}
, $2
zu, was für die ursprüngliche Schale substituierte erweitert wird $phrase
. Im Kontext werden sh
diese Parameter richtig zitiert, sodass Sie keinen Code mehr einfügen können. ( Diese andere Antwort von mir erklärt dummy
).
Auch jetzt gibt es Raum für Verbesserungen. Was ist, wenn der Satz war -f
? Der grep
Teil würde schließlich sein:
grep --with-filename --label="…" --color "-f"
es würde sich über das fehlende Argument beschweren. Verwenden Sie --
diese Option, um das Ende der Optionen anzuzeigen. -f
after --
wird nicht als Option behandelt. Gleiches gilt für pdftotext
(obwohl in Ihrem speziellen Fall jeder Pfad zu PDF mit beginnen muss, /home
damit er nicht als Option interpretiert werden kann; er kann jedoch im Allgemeinen $1
zu einem String erweitert werden, der wie eine Option aussieht). Unser sh
Aufruf ist bereits immun, da sh
Optionen vor einer Befehlszeichenfolge genommen werden können und unsere Befehlszeichenfolge nicht mit einer Option verwechselt werden kann ( sh -c -- 'pdftotext …' …
sie schadet trotzdem nicht). Robusterer Befehl:
find … -exec sh -c 'pdftotext -- "$1" - | grep --with-filename --label="$1" --color -- "$2"' dummy {} "$phrase" \;