On 08/01/2015 18:41, Shinsan Hattori wrote: > Il 6 gennaio 2015 01:40, Antonio Galea <antonio.galea@xxxxxxxxx> ha scritto: >> 2015-01-05 11:50 GMT+01:00 Flavio Stanchina <flavio@xxxxxxxxxxxxx>: >>> On 04/01/2015 02:38, Antonio Galea wrote: >>>> for f in `find src/ -name \*.jpg` >>>> do >>>> [...] >>> >>> Passando gli argomenti in questo modo, prima o poi si incappa nel limite di >>> lunghezza della riga di comando. Usate >>> >>> find src -name '*.jpg' | while read f; do ... >>> >>> ...che tra l'altro parallelizza (almeno in parte) l'operazione di lettura >>> della directory e l'effettiva elaborazione dei file. >> >> Ciao Flavio, >> grazie della utilissima puntualizzazione - nel caso in questione la >> tua versione è sicuramente necessaria, visto che le immagini possono >> essere molte migliaia. > > Non ho capito bene quale fosse il problema della precedente > formulazione ma mi fido... Se tu scrivi il comando così: for f in $(find src -name "*.jpg"); do ... la shell esegue find, raccoglie tutto l'output e poi lo usa per sostituire $(find ...) costruendo un luuuungo comando del tipo: for f in src/1.jpg src/2.jpg ...; do Il problema è che la linea di comando ha un limite di lunghezza. Si parla di centinaia di migliaia di byte, ma se hai migliaia di file e/o percorsi molto lunghi, prima o poi ci arriverai. Un problema secondario è che in questo modo prima la shell esegue find e raccoglie tutto l'output, poi inizia ad elaborare i file. Con una pipeline l'elaborazione avviene in parallelo. > Anche perché la forma proposta da Flavio > inizia a risolvere un bug che stavo cercando di estirpare da solo (per > ora senza successo): i files con spazi all'interno del nome. Mentre > inizialmente questi generavano due diversi valori per f, usando il > "find src -name '*.jpg' | while read f; do ..." questo non accade. Certo, dovresti usare for f in "$(find src -name "*.jpg")"; do ... O meglio ancora for f in "$(find src -name '*.jpg')"; do ... per evitare ambiguità con le virgolette, visto che l'argomento di find non contiene variabili da espandere. > Tuttavia il problema si ripresenta al momento in cui viene chiamato > convert: [...] > > Da quel che ho visto in giro mi è parso di capire che per evitare > questo genere di problemi la cosa migliore è indicare le variabili > nella forma "${f}", ma sembra non bastare. Ti riferisci all'uso di virgolette o alle parentesi graffe? Le virgolette servono proprio a questo, le graffe no. In caso di dubbi, "man bash" e leggiti la sezione QUOTING (tutta, anche se è lunga e complicata). > Quel che ancora non ho risolto > è il test della data, che deve tener conto della possibile differente > estensione tra i due files [...] Spiega, perché mai il test sulla data dovrebbe tener conto delle differenti estensioni? > echo "comando lanciato: convert "${1}" -filter Lanczos -define > filter:lobes=4 -resize 1920x1080\> $(echo "${2}" | sed > 's/\(.*\)\..*/\1.jpg/')" Qui il quoting è decisamente sbagliato. Prova con: echo "comando lanciato: convert \"${1}\" -filter Lanczos -define filter:lobes=4 -resize 1920x1080\> \"$(echo "${2}" | sed 's/\(.*\)\..*/\1.jpg/')\"" Peraltro, userei una variabile temporanea per il nome del file di destinazione, così semplifichi i comandi e risulta più chiaro quello che sta succedendo... > # BUG: le variabili non vengono assate correttamente, i nomi di files > contenenti spazi generano errori > convert "${1}" -filter Lanczos -define filter:lobes=4 -resize > 1920x1080\> $(echo "${2}" | sed 's/\(.*\)\..*/\1.jpg/') Il problema con gli spazi sta qui: l'argomento di destinazione deve essere tra virgolette. "$(echo "${2}" | sed 's/\(.*\)\..*/\1.jpg/')" (lasciamo come esercizio per il lettore capire perché le abbondanti virgolette non causano ambiguità di interpretazione) Inoltre, invece di mettere il backslash prima di >, metti l'argomento tra apici. > # individua tutti i files presenti nella cartella src. Poi se il mime-type > # corrisponde a image li converte chiamando resize_and_convert > find "${src}" -name '*.*' | while read f; > do > t="${dst}"${f#"${src}"} Il quoting qui è un po' incomprensibile: perché non t="${dst}${f#${src}}" ...e comunque ti conviene usare opportunamente l'opzione -printf di find per avere direttamente i percorsi relativi a src, così non devi tagliare e ricucire stringhe in modi difficili da seguire. > type=$(file --mime-type "${f}" | sed 's/\(^.*\)\(: \)\(.*\)\(\/.*\)/\3/') Invece di impazzire con regular expression complicate, usa l'opzione -b di file per omettere il nome del file, tanto lo sai già. > if [ "${type}" == "image" ] > then ...e per eliminare completamente sed sopra, usa if expr "$type" : 'image/'; then ... > ## BUG: il controllo deve tener conto che il file di origine e > ## quello di destinazione potrebbero non avere la stessa estensione > test "${f}" -nt "${t}" && resize_and_convert "${f}" "${t}" Ah, adesso capisco: il problema non è il controllo, ma il fatto che $t non contiene il nome di destinazione che poi userai davvero... Io sposterei il controllo nella funzione resize_and_convert, così generi il nome del file una volta sola. Non proseguo con appunti sul quoting nel resto dello script, perché valgono i princìpi già indicati sopra. -- Ciao, Flavio Those who do not understand Unix are condemned to reinvent it, poorly. -- Henry Spencer -- Per iscriversi (o disiscriversi), basta spedire un messaggio con OGGETTO "subscribe" (o "unsubscribe") a mailto:linuxtrent-request@xxxxxxxxxxxxx