Verwenden von Semikolon (;) vs Plus (+) mit exec in find

159

Warum gibt es einen Unterschied in der Ausgabe zwischen der Verwendung

find . -exec ls '{}' \+

und

find . -exec ls '{}' \;

Ich habe:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1
Ankur Agarwal
quelle

Antworten:

249

Dies lässt sich am besten anhand eines Beispiels veranschaulichen. Angenommen, finddiese Dateien werden angezeigt:

file1
file2
file3

Die Verwendung -execmit einem Semikolon ( find . -exec ls '{}' \;) wird ausgeführt

ls file1
ls file2
ls file3

Wenn Sie stattdessen ein Pluszeichen verwenden ( find . -exec ls '{}' \+), werden so viele Dateinamen wie möglich als Argumente an einen einzelnen Befehl übergeben:

ls file1 file2 file3

Die Anzahl der Dateinamen ist nur durch die maximale Befehlszeilenlänge des Systems begrenzt. Wenn der Befehl diese Länge überschreitet, wird der Befehl mehrmals aufgerufen.

Martin
quelle
1
Vielen Dank. Dies ist sehr nützlich, um die resultierenden Dateien sortieren zu wollen: find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas
1
Dumm q: Ich stelle fest, dass die +Verknüpfung mit -execimmer maskiert ist, die +Verknüpfung mit -mtimejedoch nicht. Kennst du den Grund? Ich denke, es ist Gewohnheit, ;mit zu entkommen -exec.
Kevinarpe
3
@ kevinarpe in der Tat, ich würde es aus Gewohnheit kalkulieren ;. Ich kann mir nicht vorstellen, dass es jemals notwendig ist zu fliehen+
Martin
36

Alle bisherigen Antworten sind richtig. Ich biete dies als eine klarere (für mich) Demonstration des Verhaltens an, das beschrieben wird, echoanstatt ls:

Mit einem Semikolon wird der Befehl echoeinmal pro gefundener Datei (oder eines anderen gefundenen Dateisystemobjekts) aufgerufen:

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

Mit einem Plus wird der Befehl echonur einmal aufgerufen. Jede gefundene Datei wird als Argument übergeben.

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

Wenn findeine große Anzahl von Ergebnissen angezeigt wird, kann es sein, dass der Befehl, der aufgerufen wird, die Anzahl der Argumente drosselt.

Johnsyweb
quelle
1
Sollten Sie die Ergebnisse nicht nur zu einer Zahl hinzufügen, die es sicher macht, sie an die Shell zu übergeben? Zumindest ist es das, was zu xargstun ist ... im Prinzip erstickt es nie an zu vielen Argumenten.
Rmano
1
@Rmano: Ich habe gesehen, dass find(und xargs) unter Solaris mehr Argumente ausgegeben werden, als verbraucht werden könnten. Die xargs(und find) in GNUs Findutils scheinen sich vernünftiger zu verhalten, aber nicht jeder verwendet GNU.
Johnsyweb
2
@ Johnsyweb, alle POSIX findwürden versuchen, die Grenze der Anzahl der Argumente nicht zu erreichen. Und dazu gehört auch Solaris (mindestens 10). Es kann fehlschlagen, wenn Sie so etwas wie find ... -exec ksh -c 'cmd "$@" "$@"' sh {} +oder tun find ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +, aber findnicht wirklich dafür verantwortlich gemacht werden können. Beachten Sie, dass GNU findeine der letzten findzu unterstützenden Implementierungen war +(früher war es schwierig, Skripte auf GNU-Systeme zu portieren).
Stephane Chazelas
18

Von man find:

-exec Befehl;

Befehl ausführen; true, wenn der Status 0 zurückgegeben wird. Alle folgenden zu findenden Argumente gelten als Argumente für den Befehl, bis ein Argument aus ';' angetroffen wird. Die Zeichenfolge '{}' wird durch den aktuellen Dateinamen ersetzt, der überall dort verarbeitet wird, wo er in den Argumenten des Befehls vorkommt, nicht nur in Argumenten, in denen er allein ist, wie in einigen Versionen von find. Diese beiden Konstruktionen müssen möglicherweise maskiert (mit einem '\') oder in Anführungszeichen gesetzt werden, um sie vor der Erweiterung durch die Shell zu schützen. Im Abschnitt BEISPIELE finden Sie Beispiele für die Verwendung der Option '-exec'. Der angegebene Befehl wird für jede übereinstimmende Datei einmal ausgeführt. Der Befehl wird im Startverzeichnis ausgeführt. Es gibt unvermeidbare Sicherheitsprobleme bei der Verwendung der Option -exec.

-exec Befehl {} +

Diese Variante der Option -exec führt den angegebenen Befehl für die ausgewählten Dateien aus. Die Befehlszeile wird jedoch erstellt, indem jeder ausgewählte Dateiname am Ende angehängt wird . Die Gesamtzahl der Aufrufe des Befehls ist viel geringer als die Anzahl der übereinstimmenden Dateien. Die Befehlszeile wird ähnlich wie xargs seine Befehlszeilen erstellt. Innerhalb des Befehls ist nur eine Instanz von '{}' zulässig. Der Befehl wird im Startverzeichnis ausgeführt.

So wie ich es verstehe, \;wird für jede gefundene Datei ein separater Befehl ausgeführt find, während \+die Dateien angehängt und für alle ein einziger Befehl ausgeführt wird. Das \ist ein Fluchtcharakter, also ist es:

ls testdir1; ls testdir2 

vs.

ls testdir1 testdir2

Das oben Genannte in meiner Shell zu tun, spiegelte die Ausgabe in Ihrer Frage wider.

Beispiel, wann Sie verwenden möchten \+

Angenommen, zwei Dateien 1.tmpund 2.tmp:

1.tmp:

1
2
3

2.tmp:

0
2
3

Mit \;:

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

Wenn Sie Folgendes verwenden \+(um die Ergebnisse von zu verketten find):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

In diesem Fall ist es also der Unterschied zwischen diff 1.tmp; diff 2.tmpunddiff 1.tmp 2.tmp

Es gibt Fälle, in denen dies \;angemessen und \+notwendig ist. Die Verwendung \+mit rmist eine solche Instanz, bei der beim Entfernen einer großen Anzahl von Dateien die Leistung (Geschwindigkeit) überlegen ist \;.

Matchew
quelle
Ich kann auch die Manpage lesen. Und ich habe es getan, aber ich glaube nicht, dass ich den Unterschied zwischen der Verwendung verstehe; vs +
Ankur Agarwal
Ich glaube nicht, dass der -1 fair war, ich erklärte mein Verständnis des Mannes. Ich habe den Mann nicht einfach kopiert und bin gegangen. Ich habe meine Antwort jedoch so bearbeitet, dass sie ein besseres Beispiel enthält.
Matchew
10

findhat spezielle Syntax. Sie verwenden das so {}wie sie sind, weil sie eine Bedeutung haben, die als Pfadname der gefundenen Datei zu finden ist, und (die meisten) Shells sie nicht anders interpretieren. Sie benötigen den Backslash, \;da das Semikolon eine Bedeutung für die Shell hat, die es auffrisst, bevor findes abgerufen werden kann. Was also findNACH dem Fertigstellen der Shell in der an das C-Programm übergebenen Argumentliste angezeigt werden soll, ist

"-exec", "rm", "{}", ";"

Sie müssen jedoch \;in der Befehlszeile ein Semikolon durch die Shell zu den Argumenten führen.

Sie können damit durchkommen, \{\}weil die Shell-zitierte Interpretation von \{\}gerecht ist {}. Ebenso könnten Sie '{}' verwenden.

Was Sie nicht tun können, ist zu verwenden

 -exec 'rm {} ;'

weil die Shell das als ein Argument interpretiert ,

"-exec", "rm {};"

und rm {} ;ist nicht der Name eines Befehls. (Zumindest, wenn nicht wirklich jemand herumschraubt.)

Aktualisieren

Der Unterschied ist zwischen

$ ls file1
$ ls file2

und

$ ls file1 file2

Das +verkettet die Namen in einer Befehlszeile.

Charlie Martin
quelle
1
Ich verstehe was du sagst. Ich frage, was ist der Unterschied zwischen der Verwendung; vs +
Ankur Agarwal
1
Entschuldigung, aber haben Sie meine Frage oder meinen Kommentar sorgfältig gelesen? Vielleicht muss ich es umformulieren. Warum gibt es ein anderes O / P, wenn ich Semikolon mit Exec in Find verwende, als wenn ich Plus mit Exec in Find verwende?
Ankur Agarwal
2
Dies ist eine hervorragende Erklärung dafür, warum der Befehl so ist, dass die akzeptierte Antwort nicht abdeckt. Vielen Dank!
Sherwin Yu
1

Der Unterschied zwischen ;(Semikolon) oder +(Pluszeichen) besteht darin, wie die Argumente an find's -exec/ -execdirparameter übergeben werden. Beispielsweise:

  • using ;führt mehrere Befehle aus (separat für jedes Argument).

    Beispiel:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    Alle folgenden Argumente findgelten als Argumente für den Befehl.

    Die Zeichenfolge {}wird durch den aktuellen Dateinamen ersetzt, der verarbeitet wird.

  • using +führt die geringstmöglichen Befehle aus (da die Argumente miteinander kombiniert werden). Es ist der Funktionsweise von Befehlen sehr ähnlich xargs, daher werden so viele Argumente wie möglich pro Befehl verwendet, um zu vermeiden, dass die maximale Anzahl von Argumenten pro Zeile überschritten wird.

    Beispiel:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    Die Befehlszeile wird erstellt, indem jeder ausgewählte Dateiname am Ende angehängt wird.

    {}Innerhalb des Befehls ist nur eine Instanz von zulässig.

Siehe auch:

Kenorb
quelle
-5

Wir haben versucht, eine Datei für die Haushaltsführung zu finden.

finden . -exec echo {} \; Befehl lief über Nacht am Ende kein Ergebnis.

finden . -exec echo {} \ + hat Ergebnisse und dauerte nur wenige Stunden.

Hoffe das hilft.

Ron
quelle
2
Diese Antwort erklärt nicht, wie diese beiden Methoden funktionieren und wie sich die von ihnen erzielten Ergebnisse unterscheiden.
misko321