Sep 232012
 

In un precedente articolo ho scritto a proposito del comando locate un comando utile per trovare rapidamente un file nel computer.
Un’alternativa a locate è il comando find : GNU find ricerca la struttura di directory radice in ciascun nome di file dato dalla valutazione dell’espressione data da sinistra a destra, secondo le regole di precedenza, fino a quando il risultato è noto, a questo punto find utilizza l’azione decisa e si muove verso il nome del file successivo.

Con find è possibile utilizzare molte opzioni con cui comporre una espressione, come azione standard viene stampato su standard output il nome del file che corrispondono all’espressione.


Ma prima di mostrare alcuni esempi utili con find e l’azione exec un po’ di teoria:

Opzioni di Find options

Le opzioni più comuni di find per cercare file sono:

-name stringa Questa è l’opzione più comune ed esegue una ricerca per i file la cui base del nome del file (il percorso con le directory iniziali rimosse) corrispondono alla stringa inserita coem parametro.

-mtime n La data di modifica del file è n*24 ore fà.

-size n[cwbkMG] Il file utilizza n unità di spazio disco. I seguenti suffissi possono essere utilizzati:

  • `b’ per blocchi da 512-byte (questo è il default se non viene messo nulla)
  • `c’ per bytes
  • `w’ per word di due-byte
  • `k’ per Kilobytes (unità di1024 bytes)
  • `M’ per Megabytes (unità di 1048576 bytes)
  • `G’ per Gigabytes (unità di 1073741824 bytes)

-uid n L’ID numerico del File è n

Azioni possibili in find

E’ possibile definire delle azioni da fare sui file che corrispondono alle espressioni di ricerca, e tra le azioni la più versatile è sicuramente l’ exec

-exec comando ; Esegue comando; ritorna true se il codice di uscita del comando è 0. Tutti gli argomenti che seguono sono considerati da find come argomenti da passare al comando fino a quando si incontro un argomento costituito da `; ‘ . La stringa `{}’ viene sostituita dal nome del file in corso di elaborazione e viene messo in ogni punto si trovi questa stringa.

Alcuni esempi con find and exec

Cercare alcuni file con find ed eliminarli con exec, questa è probabilmente una delle azioni più comuni con exec, e non si dovrebbe usarla per fare questo, leggere di seguito, ecco alcuni esempi degli utilizzi più comuni:

Cerca tutti i file con estensione .old e gli elimina:

 find / -name "*.old" -exec /bin/rm {} \;

Cerca tutti i file con dimensione > di 100 MB e li cancella:

 find / -size +100M -exec /bin/rm {} \;

A volte alcuni programmi si scatenano e creano migliaia di file di piccole dimensioni in una directoy, in questo caso non è possibile utilizzare un semplice rm * perché la shell non sarebbe in grado di gestire l’espansione del carattere * con tutti questi nomi di file, ma è possibile utilizzare find per eliminare tutti i file in una directory uno ad uno.

 find . -exec /bin/rm {} \;

Si prega di notare, che non si dovrebbe MAI utilizzare exec coem visto in questi esempi, per i casi di cancellazione di file GNU find ha l’opzione In case of -delete che è molto più sicura di un “-exec /bin/rm {} \;”. Ad esempio:

find / -name "*.old" -delete

Nei vecchi sistemi Unix non si poteva avere l’opzione -delete, e così non si aveva altra scelta che utilizzare l’opzione -exec.


Ed adesso qualche esempio di quello che potete fare con find e l’azioneexec.

Cambiare ricorsivamente i permessi sui file, senza toccare le directory.

find ./ -type f -exec chmod 644 {} \;

Con l’opzione -type f si selezionano solo i file e dopo è facile fare un chmod su di loro.

Cambiare ricorsivamente la proprietà di tutti i file da olduser a newuser

find / -user olduser  -type f  -exec chown newuser {} \;

In questo esempio ho usato l’opzione di ricerca -user, in alternativa è possibile utilizzare -uid

Ricorsivamente modifica i permessi di tutti, e solo, le directory

find . -type d -exec chmod 755 {} \;

In questo esempio ho usato di nuovo l’opzione -type con il parametro d per identificare solo le directory.

Conclusioni

Come avete visto in questi esempi il comando find con l’azione exec può ottenere azioni molto potenti, quando si ha a che fare con una determinata azione che deve essere fatta solo su un sottoinsieme di file questa può essere la combinazione vincente per voi.

Popular Posts:

Flattr this!

  13 Responses to “Linux shell, come usare l’opzione exec in find con esempi”

  1. All of the -exec example end with “{} \;” which means they would be more efficient and faster if they ended with “{} +” instead. Using ‘+’ instead of ‘;’ makes find aggregate pathnames and execute far fewer commands, instead of one command for each pathname.

    You can’t use a ‘+’ if the last command argument is not “{}”, for example you can’t do:

    find . -name “*.old” -exec mv {} oldfiles + # doesn’t work

    but there is a way around that involving the shell:

    find . -name “*.old” -exec sh -c ‘mv “$@” oldfiles’ sh {} +

    This uses two process per aggregated set of pathnames, but is still way more efficient than:

    find . -name “*.old” -exec mv {} oldfiles \;

    if there are more than a couple of files.

  2. Hi Great article. I am trying to put all my avi movies into a folder of the same name. I am happy to copy them into the directory by hand but was hoping to use find to create the directories:

    find ./ -maxdepth 1 -name “*.avi” -exec mkdir {} \;

    but it will not allow me to create a directory with the same name as the file. Any ideas how I can get around this. Thanks.

  3. To b1ackmai1er:

    Assuming that you are using BASH, I would do it in two steps

    First, load all filenames into a bash array

    IFS=$’\n’ A=( $(find . -maxdepth 1 -name \*.avi ) )

    The purpose of the IFS is to insure that ‘whitespaces’ are not interpreted by bash as a separator (e.g. without it the file ‘hello world.avi’ would become ‘hello’ and ‘wold.avi’

    You can then check the content of the array A with

    for i in “${A[@]}” ; do echo “$i” ; done

    Moving each file into a directory with the same name requires some kinds of renaming since the file and the directory cannot exist with the same name.

    Something like that could work:

    for i in “${A[@]}” ; do echo mkdir “$i.dir” && echo mv “$i” “$i.dir” && echo mv “$i.dir” “$i” ; done

    If the result looks good then just remove the 3 ‘echo’. Do not try to execute the command printed by the echo because they lack proper “quotes”

  4. find -exec is indeed useful… my of my favorites is for finding a list of files that contain a word or phrase….

    find . -exec grep -ls SearchMe {} +

    @Geoff The + .vs ; is a nice touch.

  5. Nice article, but …..

    It’s not the problem it use to be with legacy Unix (Yea Linux!), but I am surprised you didn’t mention that that the -exec option can over flow the command line if find returns too many objects. That’s where xargs comes in:

    # untested, be careful!
    find . -type f -name “*.txt”|xargs rm

    Back in the day, I wrote about it for the new defunct Sys Admin:

    http://anselmo.homeunix.net/SysAdmin-Journal/html/v12/i06/a9.htm

    • Ed wrote:

      I am surprised you didn’t mention that that the -exec option can over flow the command line if find returns too many objects

      That should not happen, which is the whole point of -exec. If you can reproduce this problem with GNU find, please report it (with clear, reproducible instructions on how to reproduce the problem!) as a bug. Thanks.

    • Ed, you are mixing up two different problems. The overflow problem is with -print and command substitution.
      The problem with -exec, as stated in the article you referred to, was efficiency. The original solution to that was xargs, but these days find has that functionality built in – you use ‘+’ instead of ‘;’ to terminate the -exec command – and is preferred because of the problems xargs has with spaces and other special characters in filenames. (GNU solved the xargs problem a different way by inventing find -print0 and xargs -0, but those aren’t as widely implemented as find’s “-exec command {} +”, and they are less efficient because of the extra xargs process and the I/O through the pipe.)

  6. Thanks for this ! I’m loving the CLI !

    Question: How can I use find to search for multiple files in one go, and then have the -exec apply to all of them ?

    For example, I tried: find / -name “*.bak” -o -name “*.bak2” -o -name “*.backup” -exec ls -l {} \;

    The result listed only the file found by the last -name, “*.backup”, instead of the complete list.

    Also, I am pretty sure there must be a cleaner way to search multiple filenames without repeating the -name. Any pointers ?

    Also, why do we have to escape the “;” if it is what the -exec command is looking for to complete ?

    Thanks for your help !

  7. @Fouad:

    Please allow me to answer your question. You need to surround the or’ed -names options with escaped parenthesis as such:

    find . \( -name “*.bak” -o -name “*.bak2” -o -name “*.backup” \) -exec ls -l {} \;

    or if using xargs:

    find . \( -name “*.bak” -o -name “*.bak2” -o -name “*.backup” \) | xargs ls -l

    I’ve never thought about it, but I think you are right about the \; ending the -exec option.

  8. Thanks for your reply Ed, however I get an error stating it is an ” invalid expression: you have used a binary operator “-o” with nothing before it”

    But then it goes on to list a few folders that have nothing to do with the criteria I asked for.

    While I’m asking, if you don’t mind, how could I specify that all the results should be files and not folders ?

    I tried -type f before the \(-name “*bak” etc) bit, but it told me the search path had to precede \(-name, I tried it after, it gave me the same invalid expression error, but still listed the folders.

    Thanks again for your help !

  9. @James:

    OK, I will do so. And I’ll look into the new find-exec functionality

    @Fauad:

    I think your problem is that you need spaces around the parenthesis: \( \)

    find . \( -name “*.bak” -o -name “*.bak2” -o -name “*.backup” \) -exec ls -l {} \;

  10. That was indeed the problem !

    Live & Learn :), in this case Ask & Learn !

    Thanks a lot Ed !

  11. Thanks for sharing! Keep it up

Leave a Reply to Chris Cancel reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

*