Jeg finner meg selv hele tiden og ser på syntaksen til

find . -name "FILENAME" -exec rm {} \; 

hovedsakelig fordi jeg ikke ser hvordan nøyaktig delen -exec fungerer. Hva er betydningen av seler, tilbakeslag og semikolon? Er det andre brukstilfeller for den syntaksen?

Kommentarer

  • @Philippos: Jeg ser poenget ditt. Vær oppmerksom på at mansidene er en referanse, dvs. nyttig for de med en forståelse av saken for å slå opp syntaksen. For noen som er nye i emnet, er de ofte kryptiske og formelle for å være nyttige. at ' er av en grunn.
  • Selv den gamle POSIX man -siden leser Et verktøynavn eller argument inneholder bare de to tegnene " {} " skal erstattes d av det nåværende stienavnet , som synes å være tilstrekkelig for meg. I tillegg har den et eksempel med -exec rm {} \;, akkurat som i spørsmålet ditt. I mine dager var det knapt andre ressurser enn " stor grå vegg ", bøker med trykte man sider (papir var billigere enn lagring). Så jeg vet at dette er tilstrekkelig for noen som er nye i emnet. Det siste spørsmålet ditt er imidlertid greit å stille her. Dessverre har verken @Kusalananda eller meg selv svar på det.
  • @Philippos 🙂 Åh, det ville være en spennende ting å prøve, for et nivå på " spennende " som er utenfor skalaen til mitt diagram.
  • Comeon @Philippos. Forteller du virkelig Kusalananda at han ikke forbedret arbeidssiden? 🙂
  • @ZsoltSzilagy Jeg fortalte det ikke eller mente det. Han matet deg veldig bra, jeg tror bare du er gammel nok til å spise alene. (-;

Svar

Dette svaret kommer i følgende deler:

  • Grunnleggende bruk av -exec
  • Bruk av -exec i kombinasjon med sh -c
  • Bruke -exec ... {} +
  • Bruke -execdir

Grunnleggende bruk av -exec

Alternativet -exec tar et eksternt verktøy med valgfrie argumenter som argumentet og utfører det.

Hvis strengen {} er til stede hvor som helst i den gitte kommandoen, vil hver forekomst av den bli erstattet av banenavnet som for tiden behandles ( f.eks. ./some/path/FILENAME). I de fleste skjell trenger ikke de to tegnene {} å siteres.

Kommandoen må avsluttes med en ; for find for å vite hvor den ender (da det kan være flere alternativer etterpå s). For å beskytte ; fra skallet, må det siteres som \; eller ";" ellers vil skallet se det som slutten på find -kommandoen.

Eksempel (\ på slutten av de to første linjene er bare for linjefortsettelser):

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} ";" 

Dette finner alle vanlige filer (-type f) hvis navn samsvarer med mønsteret *.txt i eller under den nåværende katalogen. Deretter vil den teste om strengen hello forekommer i noen av de funnet filene ved hjelp av grep -q (som ikke gir noen utgang, bare en utgang status). For de filene som inneholder strengen, vil cat kjøres for å sende innholdet av filen til terminalen.

Hver -exec fungerer også som en «test» på banenavnene som er funnet av find, akkurat som -type og -name gjør. Hvis kommandoen returnerer en nullutgangsstatus (som betyr «suksess»), blir neste del av find -kommandoen vurdert, ellers blir find kommandoen fortsetter med neste banenavn. Dette brukes i eksemplet ovenfor for å finne filer som inneholder strengen hello, men for å ignorere alle andre filer.

Eksemplet ovenfor illustrerer de to vanligste bruken tilfeller av -exec:

  1. Som en test for å begrense søket ytterligere.
  2. For å utføre en slags handling på funnet stienavn (vanligvis, men ikke nødvendigvis, på slutten av find -kommandoen).

Bruk av -exec i kombinasjon med sh -c

Kommandoen som -exec kan utføre er begrenset til et eksternt verktøy med valgfrie argumenter.Å bruke shell-innebygde funksjoner, betingelser, rørledninger, omdirigeringer osv. Direkte med -exec er ikke mulig, med mindre det er pakket inn i noe som en sh -c child shell.

Hvis bash funksjoner kreves, bruk bash -c i stedet for sh -c.

sh -c kjører /bin/sh med et skript gitt på kommandolinjen, etterfulgt av valgfrie kommandolinjeargumenter til skriptet.

Et enkelt eksempel på å bruke sh -c i seg selv, uten find :

sh -c "echo "You gave me $1, thanks!"" sh "apples" 

Dette sender to argumenter til underordnet skallskript. Disse blir plassert i $0 og $1 for at skriptet skal brukes.

  1. The streng sh. Dette vil være tilgjengelig som $0 inne i skriptet, og hvis det interne skallet sender ut en feilmelding, vil det prefikse den med denne strengen.

  2. Argumentet apples er tilgjengelig som $1 i skriptet, og hadde det vært flere argumenter, ville disse vært tilgjengelige som $2, $3 osv. De ville også være tilgjengelige i listen "$@" $0 som ikke ville være en del av "$@").

Dette er nyttig i kombinasjon med -exec da det tillater oss å lage vilkårlig komplekse skript som virker på banenavnene som er funnet av find.

Eksempel: Finn alle vanlige filer som har et bestemt suffiks for filnavn, og endre dette filnavnet til et annet suffiks, der suffiksen holdes i variabler:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c "mv "$3" "${3%.$1}.$2"" sh "$from" "$to" {} ";" 

Inne i integrasjonen rnalt skript, $1 ville være strengen text, $2 ville være strengen txt og $3 ville være det stienavnet find har funnet for oss. Parameterutvidelsen ${3%.$1} tar stienavnet og fjerner suffikset .text fra den.

Eller bruker dirname / basename:

find . -type f -name "*.$from" -exec sh -c " mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"" sh "$from" "$to" {} ";" 

eller med tilleggsvariabler i internt skript:

find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2; pathname=$3 mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"" sh "$from" "$to" {} ";" 

Merk at i denne siste variasjonen er variablene from og to i underskallet skiller seg fra variablene med samme navn i det eksterne skriptet.

Ovenfor er den riktige måten å kalle et vilkårlig komplekst skript fra -exec med find. Å bruke find i en sløyfe som

for pathname in $( find ... ); do 

er feil utsatt og uelegant (personlig mening). Det splitter filnavn på mellomrom, påkaller filnavn globbing, og tvinger også skallet til å utvide hele resultatet av find før du til og med kjører den første iterasjonen av sløyfen.

Se også:


Bruk av -exec ... {} +

; på slutten kan erstattes av +. Dette fører til at find utfører den gitte kommandoen med så mange argumenter (funnet stienavn) som mulig i stedet for en gang for hvert funnet stinavn. Strengen {} må forekomme like før + for at dette skal fungere .

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} + 

Her vil find samle de resulterende banenavnene og utføre cat på så mange av dem som mulig samtidig.

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec mv -t /tmp/files_with_hello/ {} + 

På samme måte vil mv bli utført som få ganger som mulig. Dette siste eksemplet krever GNU mv fra coreutils (som støtter -t -alternativet).

Bruk av -exec sh -c ... {} + er også en effektiv måte å løpe over et sett med banenavn med et vilkårlig komplekst skript.

Grunnleggende er det samme som når du bruker -exec sh -c ... {} ";", men skriptet tar nå en mye lengre liste over argumenter. Disse kan sløyfes ved å løkke over "$@" inne i skriptet.

Vårt eksempel fra den siste delen som endrer filnavnssuffikser:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2 shift 2 # remove the first two arguments from the list # because in this case these are *not* pathnames # given to us by find for pathname do # or: for pathname in "$@"; do mv "$pathname" "${pathname%.$from}.$to" done" sh "$from" "$to" {} + 

Bruk av -execdir

Det er også -execdir ( implementert av de fleste find varianter, men ikke et standardalternativ).

Dette fungerer som -exec med den forskjellen at den gitte shell-kommandoen kjøres med katalogen til det funnet banenavnet som den nåværende arbeidskatalogen og at {} inneholder basenavnet til det funnet stienavnet uten banen (men GNU find vil fremdeles prefikse basenavnet med ./, mens BSD find ikke vil gjøre det).

Eksempel:

find . -type f -name "*.txt" \ -execdir mv {} done-texts/{}.done \; 

Dette vil flytte hvert funnet *.txt -fil til en eksisterende done-texts underkatalog i samme katalog som der filen var funnet . Filen vil også bli omdøpt ved å legge til suffikset .done til den.

Dette ville være litt vanskeligere å gjøre med -exec da vi måtte få grunnnavnet til den funnet filen ut av {} for å danne det nye navnet på filen. Vi trenger også katalognavnet fra {} for å finne done-texts katalogen riktig.

Med -execdir, noen ting som disse blir enklere.

Den tilsvarende operasjonen bruker -exec i stedet for -execdir måtte ansette et barneskall:

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done" done" sh {} + 

eller,

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "${name%/*}/done-texts/${name##*/}.done" done" sh {} + 

Kommentarer

  • -exec tar et program og argumenter og kjører det; noen skallkommandoer består bare av et program og argumenter, men mange gjør det ikke. En skallkommando kan omfatte omdirigering og piping; -exec kan ikke (selv om hele find kan omdirigeres). En skallkommando kan bruke ; && if etc; -exec kan ikke, selv om -a -o kan gjøre noe. En skallkommando kan være et alias eller skallfunksjon, eller innebygd; -exec kan ikke. En skallkommando kan utvide vars; -exec kan ikke (selv om det ytre skallet som kjører find, kan). En skallkommando kan erstatte $(command) forskjellig hver gang; -exec kan ikke. …
  • Å si det ' en shell-kommando er feil her, find -exec cmd arg \; betyr ikke ' t påkaller et skall for å tolke en skallkommandolinje, den kjører execlp("cmd", "arg") direkte, ikke execlp("sh", "-c", "cmd arg") (som shell vil ende opp med å gjøre det som tilsvarer execlp("cmd", "arg") hvis cmd ikke var innebygd).
  • Du kan avklare at alle find argumentene etter -exec og opp til ; eller + utgjør kommandoen for å utføre sammen med argumentene, med hver forekomst av et {} argument erstattet med den nåværende filen (med ;), og {} som siste argument før + erstattet med en liste over filer som separate argumenter (i {} + sak). IOW -exec tar flere argumenter, avsluttet med en ; eller {} +.
  • @Kusalananda Ville ikke ' t ditt siste eksempel også fungerer med denne enklere kommandoen: find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'?
  • @Atralb Ja, det ville også ha fungert og hatt samme effekt som den siste koden, men i stedet for å kjøre mv i en løkke, én gang per funnet fil, utfører du både sh og mv for hver funnet fil, som vil være merkbart langsommere for store mengder filer.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *