Non ho il coraggio di rifare tutto da capo, ma l'ho scritto in risposta a Commandline Find Sed Exec . Lì il richiedente voleva sapere come spostare un intero albero, escludendo eventualmente una o due directory, e rinominare tutti i file e le directory contenenti la stringa "OLD" per contenere invece "NEW" .
Oltre a descrivere il come con minuziosa verbosità di seguito, questo metodo può anche essere unico in quanto incorpora il debug integrato. Fondamentalmente non fa nulla come scritto tranne che per compilare e salvare in una variabile tutti i comandi che crede di dover eseguire per eseguire il lavoro richiesto.
Inoltre evita esplicitamente i loop il più possibile. Oltre alla sedricerca ricorsiva di più di una corrispondenza del modello, non c'è altra ricorsione per quanto ne so.
Infine, questo è completamente nulldelimitato: non inciampa su alcun carattere in nessun nome di file tranne il null. Non penso che dovresti averlo.
A proposito, è DAVVERO veloce. Guarda:
% _mvnfind() { mv -n "${1}" "${2}" && cd "${2}"
> read -r SED <<SED
> :;s|${3}\(.*/[^/]*${5}\)|${4}\1|;t;:;s|\(${5}.*\)${3}|\1${4}|;t;s|^[0-9]*[\t]\(mv.*\)${5}|\1|p
> SED
> find . -name "*${3}*" -printf "%d\tmv %P ${5} %P\000" |
> sort -zg | sed -nz ${SED} | read -r ${6}
> echo <<EOF
> Prepared commands saved in variable: ${6}
> To view do: printf ${6} | tr "\000" "\n"
> To run do: sh <<EORUN
> $(printf ${6} | tr "\000" "\n")
> EORUN
> EOF
> }
% rm -rf "${UNNECESSARY:=/any/dirs/you/dont/want/moved}"
% time ( _mvnfind ${SRC=./test_tree} ${TGT=./mv_tree} \
> ${OLD=google} ${NEW=replacement_word} ${sed_sep=SsEeDd} \
> ${sh_io:=sh_io} ; printf %b\\000 "${sh_io}" | tr "\000" "\n" \
> | wc - ; echo ${sh_io} | tr "\000" "\n" | tail -n 2 )
<actual process time used:>
0.06s user 0.03s system 106% cpu 0.090 total
<output from wc:>
Lines Words Bytes
115 362 20691 -
<output from tail:>
mv .config/replacement_word-chrome-beta/Default/.../googlestars \
.config/replacement_word-chrome-beta/Default/.../replacement_wordstars
NOTA: quanto sopra functionrichiederà probabilmente le GNUversioni di sede findper gestire correttamente le chiamate find printfe sed -z -ee :;recursive regex test;t. Se questi non sono disponibili, la funzionalità può essere probabilmente duplicata con alcune piccole modifiche.
Questo dovrebbe fare tutto quello che volevi dall'inizio alla fine con pochissime storie. L'ho fatto forkcon sed, ma mi è stato anche praticato alcune sedtecniche di ramificazione ricorsiva Ecco, questo è il motivo per cui sono qui. È un po 'come farsi un taglio di capelli scontato in una scuola di barbiere, immagino. Ecco il flusso di lavoro:
rm -rf ${UNNECESSARY}
- Ho intenzionalmente omesso qualsiasi chiamata funzionale che potrebbe eliminare o distruggere dati di qualsiasi tipo. Hai detto che
./apppotrebbe essere indesiderato. Eliminalo o spostalo altrove in anticipo o, in alternativa, potresti creare una \( -path PATTERN -exec rm -rf \{\} \)routine findper farlo in modo programmatico, ma è tutto tuo.
_mvnfind "${@}"
- Dichiara i suoi argomenti e chiama la funzione worker.
${sh_io}è particolarmente importante in quanto salva il ritorno dalla funzione. ${sed_sep}arriva in un secondo vicino; questa è una stringa arbitraria utilizzata per fare riferimento alla sedricorsione nella funzione. Se ${sed_sep}è impostato su un valore che potrebbe essere potenzialmente trovato in uno qualsiasi dei nomi di file o percorsi su cui si è agito ... beh, non lasciarlo.
mv -n $1 $2
- L'intero albero viene spostato dall'inizio. Risparmierà un sacco di mal di testa; credimi. Il resto di quello che vuoi fare - la ridenominazione - è semplicemente una questione di metadati del filesystem. Se, ad esempio, lo spostassi da un'unità a un'altra, o oltre i confini del filesystem di qualsiasi tipo, faresti meglio a farlo subito con un comando. È anche più sicuro. Notare l'
-noclobberopzione impostata per mv; come scritto, questa funzione non verrà inserita ${SRC_DIR}dove ${TGT_DIR}esiste già.
read -R SED <<HEREDOC
- Ho trovato tutti i comandi di sed qui per risparmiare sui problemi di fuga e li ho letti in una variabile da alimentare a sed di seguito. Spiegazione di seguito.
find . -name ${OLD} -printf
- Iniziamo il
findprocesso. Con findcerchiamo solo tutto ciò che deve essere rinominato perché abbiamo già eseguito tutte le operazioni da luogo a luogo mvcon il primo comando della funzione. Piuttosto che intraprendere qualsiasi azione diretta con find, come una execchiamata, ad esempio, la usiamo invece per costruire dinamicamente la riga di comando con -printf.
%dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
- Dopo aver
findindividuato i file di cui abbiamo bisogno, crea e stampa direttamente (la maggior parte ) del comando che ci servirà per elaborare la ridenominazione. Il %dir-depthvirato all'inizio di ogni riga aiuterà a garantire che non stiamo cercando di rinominare un file o una directory nell'albero con un oggetto genitore che deve ancora essere rinominato. findutilizza tutti i tipi di tecniche di ottimizzazione per percorrere il tuo albero del filesystem e non è una cosa sicura che restituirà i dati di cui abbiamo bisogno in un ordine sicuro per le operazioni. Questo è il motivo per cui la prossima volta ...
sort -general-numerical -zero-delimited
- Ordiniamo tutto
findl'output di in base a in %directory-depthmodo che i percorsi più vicini in relazione a $ {SRC} vengano elaborati per primi. Ciò evita possibili errori che coinvolgono mvfile ing in posizioni inesistenti e riduce al minimo la necessità di cicli ricorsivi. ( in effetti, potresti avere difficoltà a trovare un loop )
sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
- Penso che questo sia l'unico ciclo dell'intero script, e si ripete solo sul secondo
%Pathstampato per ogni stringa nel caso in cui contenga più di un valore $ {OLD} che potrebbe essere necessario sostituire. Tutte le altre soluzioni che immaginavo prevedevano un secondo sedprocesso e, sebbene un ciclo breve possa non essere desiderabile, certamente batte la generazione e il fork di un intero processo.
- Quindi fondamentalmente quello che
sedfa qui è cercare $ {sed_sep}, quindi, dopo averlo trovato, lo salva e tutti i caratteri che incontra finché non trova $ {OLD}, che poi sostituisce con $ {NEW}. Quindi torna a $ {sed_sep} e cerca di nuovo $ {OLD}, nel caso si verifichi più di una volta nella stringa. Se non viene trovato, stampa la stringa modificata stdout(che poi riprende di nuovo) e termina il ciclo.
- Ciò evita di dover analizzare l'intera stringa e garantisce che la prima metà della
mvstringa di comando, che deve includere $ {OLD} ovviamente, la includa e la seconda metà venga modificata tutte le volte che è necessario cancellare il $ {OLD} nome dal mvpercorso di destinazione di.
sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
- Le due
-execchiamate qui avvengono senza un secondo fork. Nel primo, come abbiamo visto, abbiamo modificare il mvcomando come fornito da find's -printfcomando funzione come necessario modificare correttamente tutti i riferimenti di $ {OLD} a $ {NEW}, ma per farlo abbiamo dovuto usare un po' punti di riferimento arbitrari che non dovrebbero essere inclusi nell'output finale. Quindi, una volta sedterminato tutto ciò che deve fare, gli chiediamo di cancellare i suoi punti di riferimento dal buffer di blocco prima di passarlo.
E ORA SIAMO TORNATI INTORNO
read riceverà un comando simile a questo:
% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000
Sarà readin ${msg}quanto ${sh_io}può essere esaminato a piacimento al di fuori della funzione.
Freddo.
-Mike