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 sed
ricerca ricorsiva di più di una corrispondenza del modello, non c'è altra ricorsione per quanto ne so.
Infine, questo è completamente null
delimitato: 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 function
richiederà probabilmente le GNU
versioni di sed
e find
per gestire correttamente le chiamate find printf
e sed -z -e
e :;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 fork
con sed
, ma mi è stato anche praticato alcune sed
tecniche 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
./app
potrebbe essere indesiderato. Eliminalo o spostalo altrove in anticipo o, in alternativa, potresti creare una \( -path PATTERN -exec rm -rf \{\} \)
routine find
per 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 sed
ricorsione 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'
-noclobber
opzione 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
find
processo. Con find
cerchiamo solo tutto ciò che deve essere rinominato perché abbiamo già eseguito tutte le operazioni da luogo a luogo mv
con il primo comando della funzione. Piuttosto che intraprendere qualsiasi azione diretta con find
, come una exec
chiamata, 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
find
individuato i file di cui abbiamo bisogno, crea e stampa direttamente (la maggior parte ) del comando che ci servirà per elaborare la ridenominazione. Il %dir-depth
virato 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. find
utilizza 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
find
l'output di in base a in %directory-depth
modo che i percorsi più vicini in relazione a $ {SRC} vengano elaborati per primi. Ciò evita possibili errori che coinvolgono mv
file 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
%Path
stampato 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 sed
processo e, sebbene un ciclo breve possa non essere desiderabile, certamente batte la generazione e il fork di un intero processo.
- Quindi fondamentalmente quello che
sed
fa 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
mv
stringa 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 mv
percorso di destinazione di.
sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
- Le due
-exec
chiamate qui avvengono senza un secondo fork
. Nel primo, come abbiamo visto, abbiamo modificare il mv
comando come fornito da find
's -printf
comando 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 sed
terminato 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à read
in ${msg}
quanto ${sh_io}
può essere esaminato a piacimento al di fuori della funzione.
Freddo.
-Mike