2013-03-26 2 views
27

Ich möchte den Dateinamen Teil einer Suche auf der Linux-Befehlszeile aussprechen. Ich habe versucht, die folgenden verwenden:Suchen und Basisname nicht gut wiedergegeben

find www/*.html -type f -exec sh -c "echo $(basename {})" \; 

und

find www/*.html -type f -exec sh -c "echo `basename {}`" \; 

und eine ganze Reihe von anderen Kombinationen von Flucht und verschiedene Teile des Textes zu zitieren. Das Ergebnis ist, dass der Pfad nicht entfernt wird:

www/channel.html 
www/definition.html 
www/empty.html 
www/index.html 
www/privacypolicy.html 

Warum nicht?

Update: Während ich eine funktionierende Lösung unten habe, bin ich immer noch daran interessiert, warum "Basisname" nicht tut, was es tun soll.

Antwort

40

Der Ärger mit dem ursprünglichen Versuch:

find www/*.html -type f -exec sh -c "echo $(basename {})" \; 

ist, dass der Code $(basename {}) einmal ausgeführt wird, bevor der Befehl find Exec ist utet. Die Ausgabe der einzelnen basename ist {}, da dies der Basisname {} als Dateiname ist. So ist der Befehl, der von Fund ausgeführt ist:

sh -c "echo {}" 

für jede Datei gefunden, aber find ersetzt eigentlich die ursprünglichen (unveränderte) Dateinamen jedes Mal, weil die {} Zeichen in der Zeichenfolge erscheinen ausgeführt werden.

Wenn Sie es wollen, arbeiten Sie einfache Anführungszeichen statt doppelte Anführungszeichen verwenden:

find www/*.html -type f -exec sh -c 'echo $(basename {})' \; 

jedoch machen echo Wiederholung auf die Standardausgabe, was basename ohnehin auf die Standardausgabe geschrieben hätte ein wenig sinnlos:

find www/*.html -type f -exec sh -c 'basename {}' \; 

und wir können das noch weiter senken, natürlich, zu:

find www/*.html -type f -exec basename {} \; 

Können Sie hier auch den Unterschied zwischen einfachen und doppelten Anführungszeichen erklären?

Dies ist Routine Shell-Verhalten. Nehmen wir einen etwas anderen Befehl (aber nur leicht - die Namen der Dateien könnten sich irgendwo unter dem Verzeichnis www befinden, nicht nur eine Ebene tiefer), und schauen Sie sich die Single-Quote (SQ) - und Double-Quote (DQ) -Versionen an der Befehl:

find www -name '*.html' -type f -exec sh -c "echo $(basename {})" \; # DQ 
find www -name '*.html' -type f -exec sh -c 'echo $(basename {})' \; # SQ 

Die einfachen Anführungszeichen übergeben das Material, das direkt dem Befehl beigefügt wird.Somit wird die Schale in der SQ-Befehlszeile, die find startet entfernt die umschließenden Anführungszeichen und der find Befehl sieht seine $9 Argument wie:

echo $(basename {}) 

, da die Shell die Anführungszeichen entfernt. Zum Vergleich: Das Material in den Anführungszeichen wird von der Shell verarbeitet. So wird in der DQ-Befehlszeile, die Schale (das startet find - nicht das startete man vonfind) sieht den $(basename {}) Teil der Zeichenfolge und führt es aus, zurück {} bekommen, so dass die Saite übergibt sie an find als $9 Argument ist:

echo {} 

Nun, wenn find seine tut -exec Aktion, in beiden Fällen ist es die {} durch den Dateinamen ersetzt, die es (zum Zwecke der Beweisführung, www/pics/index.html) nur gefunden. So erhalten Sie zwei verschiedene Befehle ausgeführt werden:

sh -c 'echo $(basename www/pics/index.html)' # SQ 
sh -c "echo www/pics/index.html"    # DQ 

Es gibt eine (leichte) Notations Betrüger dort passiert - das sind die entsprechenden Befehle, die Sie an der Shell-Typ würde. Die $2 der Shell, die gestartet wird, enthält in beiden Fällen keine Anführungszeichen - die gestartete Shell sieht keine Anführungszeichen.

Wie Sie sehen können, gibt der DQ-Befehl einfach den Dateinamen wieder; Der Befehl SQ führt den Befehl basename aus, erfasst seine Ausgabe und gibt dann die erfasste Ausgabe wieder. Ein wenig reduktionistisches Denken zeigt, dass der DQ-Befehl als -print anstelle von -exec geschrieben werden könnte, und der SQ-Befehl könnte als -exec basename {} \; geschrieben werden.

Wenn Sie GNU verwenden find, es unterstützt die -printf Aktion, die von Format Directives so verfolgt werden kann, dass basename läuft nicht notwendig ist. Dies ist jedoch nur in GNU find verfügbar; Der Rest der Diskussion gilt für jede Version von find, auf die Sie wahrscheinlich treffen werden.

+0

Dies ist eine großartige Antwort und Erklärung.Könnten Sie auch den Unterschied zwischen einfachen Anführungszeichen und Anführungszeichen hier erklären? –

+1

Vielen Dank für das Hinzufügen der Sachen über die Zitate –

+0

Es gibt offensichtlich keinen Grund, 'sh -c' hier zu verwenden, aber' -exec sh -c 'echo "$ (Basisname" {} ")"' \; "sollte immer noch Anführungszeichen enthalten, um das Teilen von Wörtern zu verhindern. in diesem Fall wäre '-exec sh -c 'echo" $ (Basisname "$ 1") "' sh {} \;' wirklich vorzuziehen. – BroSlow

10

Versuchen Sie stattdessen:

find www/*.html -type f -printf '%f\n' 

Wenn Sie es mit einem Rohr (mehr Ressourcen erforderlich) zu tun:

find www/*.html -type f -print0 | xargs -0 -n1 basename 
+0

Danke, aber ich habe das gleiche Ergebnis :( –

+0

Beitrag editiert entsprechend Tty wieder –

+0

Ok, das funktioniert, dank Jede Idee, was mit Basisnamen falsch ist –

6

Das ist, wie ich Batch-Dateien mit imagick der Größe, von der Quelle rediving Ausgabedatei

find . -name header.png -exec sh -c 'convert -geometry 600 {} $(dirname {})/$(basename {} ".png")_mail.png' \; 
+0

Sehr nützliches Snippet, danke –

0

Ich hatte mit sh zur Vermeidung von Schleifen über findet die Ausgabe und Verwendung finden wich erwähnt im Anschluss an den Praktiken etwas ähnliches, und finde zu erreichen Diese Probleme mit {} und -printf vollständig.

Sie können es so versuchen:

find www/*.html -type f -exec sh -c 'echo $(basename $1)' find-sh {} \; 

Die Zusammenfassung ist „Do not Referenz {} direkt innerhalb eines sh -c sondern es passieren als Argument an sh -c, dann können Sie referenzieren Sie es mit einer Variablen innerhalb von sh -c "die find-sh ist nur da als Dummy, um die $0 aufzunehmen, gibt es mehr Dienstprogramm, es so zu tun und mit {} für $1.

Ich nehme an, die Verwendung von echo ist wirklich, um das Konzept und die Testfunktion zu vereinfachen.Es gibt einfachere Möglichkeiten zum einfachen Echo, wie andere bereits erwähnt haben. Ein idealer Anwendungsfall für dieses Szenario könnte jedoch die Verwendung von cp, mv oder komplexeren Befehlen sein, bei denen Sie die gefundenen Dateinamen mehr als einmal im Befehl referenzieren möchten um den Weg loszuwerden, z. wenn Sie den Dateinamen sowohl in Quelle als auch in Ziel angeben müssen oder wenn Sie Dinge umbenennen.

So zum Beispiel, wenn man will nur die HTML-Dokumente auf Ihr public_html Verzeichnis kopieren (Warum denn Beispiel?!), Dann könnten Sie:

find www/*.html -type f -exec sh -c 'cp /var/www/$(basename $1) /home/me/public_html/$(basename $1)' find-sh {} \; 

Drüben auf Unix-Stack, Antwort Benutzer Wildcard des auf Looping mit Find geht in einige tolle Edelsteine ​​über die Verwendung von -exec und sh -c. (Sie können es hier finden: https://unix.stackexchange.com/questions/321697/why-is-looping-over-finds-output-bad-practice).

Verwandte Themen