Jeg finder mig selv konstant i at kigge på syntaksen for

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

primært fordi jeg ikke kan se, hvordan nøjagtigt -exec -delen fungerer. Hvad er betydningen af seler, tilbageslag og semikolon? Er der andre brugssager til denne syntaks?

Kommentarer

  • @Philippos: Jeg kan se dit punkt. Husk, at mandsiderne er en reference, dvs. nyttige for dem med en forståelse af sagen for at slå op på syntaksen. For nogen, der er nye inden for emnet, er de ofte kryptiske og formelle for at være nyttige. Du finder ud af, at det accepterede svar er ca. 10 gange så lang som mandsideposten, og at ' er af en grund.
  • Selv den gamle POSIX man side læser Et værktøjsnavn eller argument der kun indeholder de to tegn " {} " skal erstattes d ved det nuværende stinavn , som synes at være tilstrækkelig for mig. Derudover har det et eksempel med -exec rm {} \;, ligesom i dit spørgsmål. I mine dage var der næppe andre ressourcer end " store grå mur ", trykte bøger man sider (papir var skarpere end opbevaring). Så jeg ved, at dette er tilstrækkeligt for nogen, der er nye inden for emnet. Dit sidste spørgsmål er dog rimeligt at stille her. Uheldigvis har hverken @Kusalananda eller jeg selv noget svar på det.
  • @Philippos 🙂 Åh, det ville være en spændende ting at prøve på et niveau på " spændende " der er uden for skalaen for mit diagram.
  • Comeon @Philippos. Fortæller du virkelig Kusalananda, at han ikke forbedrede arbejdssiden? 🙂
  • @ZsoltSzilagy Jeg fortalte det ikke eller mente det. Han fodrede dig meget godt, jeg synes bare du er gammel nok til at spise alene. (-;

Svar

Dette svar kommer i følgende dele:

  • Grundlæggende brug af -exec
  • Brug af -exec i kombination med sh -c
  • Brug af -exec ... {} +
  • Brug af -execdir

Grundlæggende brug af -exec

Indstillingen -exec tager et eksternt værktøj med valgfri argumenter som dets argument og udfører det.

Hvis strengen {} er til stede hvor som helst i den givne kommando, erstattes hver forekomst af den stienavn, der aktuelt behandles ( fx ./some/path/FILENAME). I de fleste skaller behøver de to tegn {} ikke at blive citeret.

Kommandoen skal afsluttes med en ; for find for at vide, hvor den ender (da der kan være flere muligheder bagefter s). For at beskytte ; fra skallen skal den citeres som \; eller ";" ellers ser skallen det som slutningen af find kommandoen.

Eksempel (\ i slutningen af de to første linjer er kun for linjefortsætninger):

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

Dette finder alle almindelige filer (-type f) hvis navne matcher mønsteret *.txt i eller under den aktuelle bibliotek. Derefter tester det, om strengen hello forekommer i nogen af de fundne filer ved hjælp af grep -q (som ikke producerer nogen output, bare en exit status). For de filer, der indeholder strengen, udføres cat for at sende indholdet af filen til terminalen.

Hver -exec fungerer også som en “test” på stienavne fundet af find, ligesom -type og -name gør. Hvis kommandoen returnerer nul udgangsstatus (betyder “succes”), betragtes den næste del af find kommandoen, ellers betragtes find kommandoen fortsætter med det næste stinavn. Dette bruges i eksemplet ovenfor for at finde filer, der indeholder strengen hello, men for at ignorere alle andre filer.

Ovenstående eksempel illustrerer de to mest almindelige anvendelser tilfælde af -exec:

  1. Som en test for yderligere at begrænse søgningen.
  2. At udføre en slags handling på det fundne stienavn (normalt, men ikke nødvendigvis, i slutningen af find kommandoen).

Brug af -exec i kombination med sh -c

Den kommando, som -exec kan udføre, er begrænset til et eksternt værktøj med valgfri argumenter.At bruge shell-indbyggede, funktioner, betingelser, rørledninger, omdirigeringer osv. Direkte med -exec er ikke mulig, medmindre det er pakket ind i noget som en sh -c child shell.

Hvis bash -funktioner er påkrævet, skal du bruge bash -c i stedet for sh -c.

sh -c kører /bin/sh med et script givet på kommandolinjen, efterfulgt af valgfri kommandolinjeargumenter til dette script.

Et simpelt eksempel på at bruge sh -c i sig selv uden find :

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

Dette sender to argumenter til underordnet shell-script. Disse placeres i $0 og $1 for at scriptet skal bruges.

  1. streng sh. Dette vil være tilgængeligt som $0 inde i scriptet, og hvis den interne shell udsender en fejlmeddelelse, vil den præfikse den med denne streng.

  2. Argumentet apples er tilgængeligt som $1 i scriptet, og hvis der var flere argumenter, ville disse have været tilgængelige som $2, $3 osv. De ville også være tilgængelige på listen "$@" $0 som ikke ville være en del af "$@").

Dette er nyttigt i kombination med -exec, da det giver os mulighed for at lave vilkårligt komplekse scripts, der virker på de stienavne, der findes af find.

Eksempel: Find alle almindelige filer, der har et bestemt filnavn-suffiks, og skift filnavn-suffikset til et andet suffiks, hvor suffikserne 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" {} ";" 

Inde i integrationen rnalt script, $1 ville være strengen text, $2 ville være strengen txt og $3 ville være det sti, som find har fundet for os. Parameterudvidelsen ${3%.$1} tager stienavnet og fjerner suffikset .text fra det.

Eller ved hjælp af dirname / basename:

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

eller med tilføjede variabler i internt script:

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" {} ";" 

Bemærk, at variablerne from og i underskallen adskiller sig fra variablerne med de samme navne i det eksterne script.

Ovenstående er den korrekte måde at kalde et vilkårligt komplekst script på fra -exec med find. Brug af find i en løkke som

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

er fejlbehæftet og uelegant (personlig mening). Det splitter filnavne på mellemrum, påkalder filnavn globbing og tvinger også skallen til at udvide det komplette resultat af find, før den endda kører den første iteration af sløjfen.

Se også:


Brug af -exec ... {} +

; i slutningen kan erstattes af +. Dette får find til at udføre den givne kommando med så mange argumenter (fundne stinavne) som muligt snarere end en gang for hvert fundne stinavn. Strengen {} skal forekomme lige før + for at dette skal fungere .

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

Her vil find samle de resulterende stienavne og udføre cat på så mange af dem som muligt på én gang.

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

Ligeledes her vil mv blive udført som få gange som muligt. Dette sidste eksempel kræver GNU mv fra coreutils (som understøtter -t -indstillingen).

Brug af -exec sh -c ... {} + er også en effektiv måde at løbe over et sæt stienavne med et vilkårligt komplekst script.

Det grundlæggende er det samme som når du bruger -exec sh -c ... {} ";", men scriptet tager nu en meget længere liste over argumenter. Disse kan overføres ved at løkke over "$@" inde i scriptet.

Vores eksempel fra det sidste afsnit, der ændrer filnavnesuffikser:

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" {} + 

Brug af -execdir

Der er også -execdir ( implementeret af de fleste find varianter, men ikke en standardindstilling).

Dette fungerer som -exec med den forskel, at den givne shell-kommando udføres med biblioteket for det fundne stinavn som dets aktuelle arbejdsmappe, og at {} indeholder basenavnet på det fundne stinavn uden dets sti (men GNU find vil stadig præfix basenavnet med ./, mens BSD find ikke gør det).

Eksempel:

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

Dette vil flytte hver fundet *.txt -fil til en allerede eksisterende done-texts underkatalog i samme bibliotek som hvor filen var fundet . Filen omdøbes også ved at tilføje suffikset .done til det.

Dette ville være lidt vanskeligere at gøre med -exec da vi bliver nødt til at få basisnavnet på den fundne fil ud af {} for at danne det nye navn på filen. Vi har også brug for katalognavnet fra {} for at finde done-texts -mappen korrekt.

Med -execdir, nogle ting som disse bliver lettere.

Den tilsvarende handling ved hjælp af -exec i stedet for -execdir skal bruge en underordnet shell:

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 tager et program og argumenter og kører det; nogle shell-kommandoer består kun af et program og argumenter, men mange gør det ikke. En shell-kommando kan omfatte omdirigering og piping; -exec kan ikke (selvom hele find kan omdirigeres). En shell-kommando kan bruge ; && if osv. -exec kan ikke, selvom -a -o kan gøre noget. En shell-kommando kan være et alias eller shell-funktion eller indbygget; -exec kan ikke. En shell-kommando kan udvide vars; -exec kan ikke (selvom den ydre skal, der kører find, kan). En shell-kommando kan erstatte $(command) forskelligt hver gang; -exec kan ikke. …
  • At sige det ' en shell-kommando er forkert her, find -exec cmd arg \; betyder ikke ' t påberåber en shell for at fortolke en shell-kommandolinje, den kører execlp("cmd", "arg") direkte, ikke execlp("sh", "-c", "cmd arg") (for hvilken shell ville ende med at udføre det ækvivalente med execlp("cmd", "arg") hvis cmd ikke var indbygget).
  • Du kunne præcisere, at alle find argumenter efter -exec og op til ; eller + udgør kommandoen til at udføre sammen med sine argumenter med hver forekomst af et {} argument erstattet med den aktuelle fil (med ;) og {} som det sidste argument før + erstattet med en liste over filer som separate argumenter (i {} + sag). IOW -exec tager flere argumenter, afsluttet med en ; eller {} +.
  • @Kusalananda Ville ' ikke dit sidste eksempel også arbejde med denne enklere kommando: find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'?
  • @Atralb Ja, det ville også have fungeret og haft samme effekt som det sidste stykke kode, men i stedet for at køre mv i en løkke, en gang pr. fundet fil, udfører du både sh og mv for hver fundet fil, som vil være mærkbart langsommere for store mængder filer.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *