Ik heb beide commandos geprobeerd en het commando find | grep "filename"
is vele malen langzamer dan het simpele find "filename"
commando.
Wat zou een goede verklaring zijn voor dit gedrag?
Reacties
- Jij geven een lijst van elk bestand met find en geven de gegevens vervolgens door aan grep om te verwerken. Met find gebruikt erop ‘ eigen, mis je de stap van het doorgeven van elk weergegeven bestand aan grep om de uitvoer te ontleden. Dit zal daarom sneller gaan.
- Langzamer in welke zin? Duurt het voltooien van de opdrachten een andere hoeveelheid tijd?
- Ik kan dit ‘ niet lokaal reproduceren.
time find "$HOME" -name '.profile'
rapporteert in ieder geval een langere tijd dantime find "$HOME" | grep -F '.profile'
. (17s vs. 12s). - @JenniferAnderson Ik heb beide herhaaldelijk gerend. De 17 en 12 seconden zijn gemiddelden. En ja, de
grep
-variant komt overal in hetfind
-resultaat overeen, terwijl het matchen metfind -name
zou alleen exact overeenkomen (in dit geval). - Ja,
find filename
zou snel zijn . Ik nam een beetje aan dat dit een typfout was en dat het OPfind -name filename
betekende. Metfind filename
, zou alleenfilename
worden onderzocht (en niets anders).
Antwoord
(Ik “neem aan dat GNU find
hier)
Alleen gebruiken
find filename
zou snel zijn, omdat het alleen filename
zou retourneren, of de namen binnen filename
als het een map is, of een foutmelding als die naam niet bestond in de huidige map. Het “is een zeer snelle bewerking, vergelijkbaar met ls filename
(maar recursief als filename
een directory is).
In contrast,
find | grep filename
zou find
toestaan om een lijst met alle namen te genereren van de huidige directory en lager, die grep
dan zou filteren. Dit zou duidelijk een veel langzamere operatie zijn.
Ik neem aan dat wat eigenlijk was bedoeld was
find . -type f -name "filename"
Dit zou zoeken naar filename
als de naam van een normaal bestand ergens in de huidige directory of lager.
Dit zal net zo snel (of vergelijkbaar snel) zijn als find | grep filename
, maar de grep
oplossing zou filename
matchen met het volledige pad van elke gevonden naam, vergelijkbaar met wat -path "*filename*"
zou doen met find
.
De verwarring komt voort uit een misverstand over hoe find
werkt.
Het hulpprogramma gebruikt een aantal paden en retourneert alle namen onder deze paden.
Je mag dan beperk de geretourneerde namen met behulp van verschillende tests die kunnen werken op de bestandsnaam, het pad, het tijdstempel, de bestandsgrootte, het bestandstype, enz.
Als je zegt
find a b c
je vraagt find
om elke naam te vermelden die beschikbaar is onder de drie paden a
, b
en c
. Als dit namen zijn van reguliere bestanden in de huidige directory, dan worden deze geretourneerd. Als een van deze de naam van een directory is, wordt deze teruggestuurd samen met alle andere namen in die directory.
Wanneer ik dat doe
find . -type f -name "filename"
Dit genereert een lijst met alle namen in de huidige directory (.
) en lager. Vervolgens beperkt het de namen tot die van gewone bestanden, d.w.z. geen mappen enz., Met -type f
. Dan is er nog een beperking voor namen die overeenkomen met filename
met -name "filename"
. De tekenreeks filename
kan een globbing-patroon voor bestandsnamen zijn, zoals *.txt
(vergeet niet om het te citeren!).
Voorbeeld:
Het volgende lijkt het bestand met de naam .profile
in mijn homedirectory “te vinden”:
$ pwd /home/kk $ find .profile .profile
Maar in feite retourneert het alleen alle namen op het pad .profile
(er is maar één naam, en dat is van dit bestand).
Vervolgens cd
een niveau hoger en probeer het opnieuw:
$ cd .. $ pwd /home $ find .profile find: .profile: No such file or directory
De find
commando kan nu geen pad vinden met de naam .profile
.
Als ik het echter naar de huidige map laat kijken en de teruggezonden namen beperk tot alleen .profile
, vindt het vanaf daar ook:
$ pwd /home $ find . -name ".profile" ./kk/.profile
Reacties
Antwoord
Niet-technische uitleg: Jack zoeken in een menigte is sneller dan iedereen in een menigte zoeken en alles buiten beschouwing laten, behalve Jack.
Opmerkingen
- Het probleem is dat het OP verwacht dat Jack wees de enige persoon in de menigte. Als dat het geval is, hebben ze ‘ geluk.
find jack
zaljack
weergeven als het ‘ een bestand is met de naamjack
, of alle namen in de directory als deze ‘ een directory is. Het ‘ is een misverstand over hoefind
werkt.
Antwoord
Ik heb het probleem nog niet begrepen, maar kan wat meer inzichten geven.
Net als voor Kusalananda de find | grep
oproep is duidelijk sneller op mijn systeem, wat niet veel logisch is. In eerste instantie ging ik uit van een soort bufferprobleem; dat schrijven naar de console de tijd tot de volgende syscall voor het lezen van de volgende bestandsnaam vertraagt. Het schrijven naar een pipe is erg snel: ongeveer 40MiB / s zelfs voor 32-byte schrijfbewerkingen (op mijn nogal trage systeem; 300 MiB / s voor een blokgrootte van 1MiB). Dus ging ik ervan uit dat find
sneller uit het bestandssysteem kan lezen bij het schrijven naar een pipe (of bestand), zodat de twee bewerkingen voor het lezen van bestandspaden en het schrijven naar de console parallel kunnen draaien ( wat find
als een enkel thread-proces niet op zichzelf kan doen.
Het is find
“s fout
De twee aanroepen vergelijken
:> time find "$HOME"/ -name "*.txt" >/dev/null real 0m0.965s user 0m0.532s sys 0m0.423s
en
:> time find "$HOME"/ >/dev/null real 0m0.653s user 0m0.242s sys 0m0.405s
laat zien dat find
iets ongelooflijk stoms doet (wat dat ook mag zijn). blijkt nogal incompetent te zijn in het uitvoeren van -name "*.txt"
.
Kan afhangen van de input / output-verhouding
Je zou kunnen denken dat find -name
wint als er heel weinig te schrijven valt. Maar het wordt alleen maar gênanter voor find
. Het verliest zelfs als er helemaal niets te schrijven is tegen 200K bestanden (13M pipe-gegevens) voor grep
:
time find /usr -name lwevhewoivhol
find
kan zo snel zijn als grep
, maar
Het blijkt dat find
“s domheid met name
zich niet uitstrekt tot andere tests. Gebruik in plaats daarvan een regex en het probleem is verdwenen:
:> time find "$HOME"/ -regex "\.txt$" >/dev/null real 0m0.679s user 0m0.264s sys 0m0.410s
Ik denk dat dit als een bug kan worden beschouwd. Is er iemand die een bugrapport wil indienen? Mijn versie is find (GNU findutils) 4.6.0
Reacties
- Hoe herhaalbaar zijn je timings? Als je eerst de
-name
-test hebt gedaan, dan kan het langzamer zijn geweest omdat de inhoud van de directory niet in de cache werd opgeslagen. (Bij het testen van-name
en-regex
vind ik dat ze ongeveer dezelfde tijd in beslag nemen, tenminste als er eenmaal rekening is gehouden met het cache-effect. Of het kan natuurlijk gewoon een andere versie zijn vanfind
…) - @psmears Natuurlijk heb ik deze tests verschillende keren gedaan. Het cacheprobleem is al genoemd in de opmerkingen bij de vraag vóór het eerste antwoord. Mijn
find
versie is find (GNU findutils) 4.6.0 - Waarom is het verrassend dat het toevoegen van
-name '*.txt'
langzamer gaatfind
? Het moet extra werk doen, elke bestandsnaam testen. - @Barmar Enerzijds kan dit extra werk extreem snel worden gedaan. Anderzijds scheelt dit extra werk ander werk.
find
hoeft minder gegevens te schrijven. En het schrijven naar een pipe is een veel langzamere bewerking. - Het schrijven naar een schijf is erg traag, het schrijven naar een pipe is niet zo erg, het kopieert alleen naar een kernelbuffer. Merk op dat het schrijven van meer naar
/dev/null
in uw eerste test op de een of andere manier minder systeemtijd heeft gebruikt.
Antwoord
Opmerking : ik neem aan dat je bedoelt find . -name filename
(anders “zoek je naar verschillende dingen; find filename
kijkt eigenlijk naar een pad met de naam bestandsnaam , dat bevat mogelijk bijna geen bestanden en wordt daarom erg snel afgesloten).
Stel dat u een directory heeft met vijfduizend bestanden. Op de meeste bestandssystemen worden deze bestanden feitelijk opgeslagen in een boom structuur , die het mogelijk maakt om snel een bepaald bestand te lokaliseren.
Dus als je find
vraagt om een bestand te zoeken waarvan de naam alleen gecontroleerd hoeft te worden, zal find
vragen aan voor dat bestand, en alleen dat bestand, naar het onderliggende bestandssysteem, dat zeer weinig paginas van de massaopslag zal lezen. Dus als het bestandssysteem zijn zout waard is, zal deze operatie veel sneller draaien dan de hele boom doorkruisen om alle items op te halen.
Als je om gewone find
vraagt, maar dat is precies wat je doet, doorkruis je de hele boom en lees je. Elke. Enkele. Invoer. Met grote mappen, dit kan een probleem zijn (het is precies de reden waarom verschillende software, die veel bestanden op schijf moeten opslaan, “directory-trees” zullen creëren met twee of drie componenten diep: op deze manier hoeft elk blad slechts minder bestanden te bevatten) .
Antwoord
Laten we aannemen dat het bestand / john / paul / george / ringo / beatles bestaat en het bestand waarnaar je zoekt heet “stenen”
find / stones
zoeken zal “beatles” vergelijken met “stenen” en laten vallen wanneer de “s” en “b” niet overeenkomen .
find / | grep stones
In dit geval zal find “/ john / paul / george / ringo / beatles” doorgeven aan grep en grep wil Ik moet het hele pad doorlopen voordat ik kan bepalen of het een match is.
grep doet daarom veel meer werk en daarom duurt het langer
Opmerkingen
- Heb je dat geprobeerd?
- De kosten van de stringvergelijkingen (extreem eenvoudig en goedkoop) vallen volledig in het niet bij de IO (of gewoon syscall indien in cache) van de directory-lookups.
- grep isn ‘ ta stringvergelijking, de vergelijking van reguliere expressies, wat betekent dat het zich een weg moet banen door de hele string totdat het ofwel vindt een wedstrijd of bereikt het einde. De zoekacties in de directory zijn altijd hetzelfde.
- @Paranoid Hm, over welke versie van find heb je het? Het ‘ is blijkbaar niet zoiets als de find die ik ‘ m gebruikt in debian.
find filename
zou alleenfilename
retourneren alsfilename
niet van het type directory was (of van het type directory, maar had zelf geen vermelding)