No tengo el corazón para hacerlo de nuevo, pero escribí esto en respuesta a Commandline Find Sed Exec . Allí, el autor de la pregunta quería saber cómo mover un árbol completo, posiblemente excluyendo uno o dos directorios, y cambiar el nombre de todos los archivos y directorios que contienen la cadena "OLD" para que en su lugar contengan "NEW" .
Además de describir el cómo con minuciosa verbosidad continuación, este método también puede ser único en el sentido de que incorpora la depuración incorporada. Básicamente, no hace nada en absoluto como está escrito, excepto compilar y guardar en una variable todos los comandos que cree que debería hacer para realizar el trabajo solicitado.
También evita explícitamente los bucles tanto como sea posible. Además de la sed
búsqueda recursiva de más de una coincidencia del patrón, no hay otra recursividad que yo sepa.
Y por último, esto está completamente null
delimitado: no se tropieza con ningún carácter en ningún nombre de archivo excepto en el null
. No creo que debas tener eso.
Por cierto, esto es REALMENTE rápido. Mira:
% _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: Lo anterior function
es probable que requieren GNU
versiones de sed
y find
para manejar apropiadamente la find printf
e sed -z -e
y:;recursive regex test;t
llamadas. Si estos no están disponibles para usted, es probable que la funcionalidad se pueda duplicar con algunos ajustes menores.
Esto debería hacer todo lo que quería de principio a fin con muy poco alboroto. Lo hice fork
con sed
, pero también estaba practicando algunas sed
técnicas recursivas de ramificación y por eso estoy aquí. Es como conseguir un corte de pelo con descuento en una escuela de peluquería, supongo. Aquí está el flujo de trabajo:
rm -rf ${UNNECESSARY}
- Dejé de lado intencionalmente cualquier llamada funcional que pudiera borrar o destruir datos de cualquier tipo. Mencionas que eso
./app
podría no ser deseado. Elimínelo o muévalo a otro lugar de antemano o, alternativamente, puede crear una \( -path PATTERN -exec rm -rf \{\} \)
rutina find
para hacerlo de forma programática, pero esa es toda suya.
_mvnfind "${@}"
- Declare sus argumentos y llame a la función de trabajo.
${sh_io}
es especialmente importante porque guarda el retorno de la función. ${sed_sep}
viene en un segundo cercano; esta es una cadena arbitraria utilizada para hacer referencia a sed
la recursividad en la función. Si ${sed_sep}
se establece en un valor que podría encontrarse en cualquiera de sus rutas o nombres de archivo sobre los que se actúa ... bueno, simplemente no lo permita.
mv -n $1 $2
- Todo el árbol se mueve desde el principio. Ahorrará muchos dolores de cabeza; créame. El resto de lo que quiere hacer, el cambio de nombre, es simplemente una cuestión de metadatos del sistema de archivos. Si, por ejemplo, estuviera moviendo esto de una unidad a otra, o cruzando los límites del sistema de archivos de cualquier tipo, es mejor que lo haga de una vez con un comando. También es más seguro. Tenga en cuenta la
-noclobber
opción establecida para mv
; como está escrito, esta función no pondrá ${SRC_DIR}
donde ${TGT_DIR}
ya existe.
read -R SED <<HEREDOC
- Ubiqué todos los comandos de sed aquí para evitar problemas de escape y los leí en una variable para alimentar a sed a continuación. Explicación a continuación.
find . -name ${OLD} -printf
- Comenzamos el
find
proceso. Con find
solo buscamos cualquier cosa que necesite cambiar de nombre porque ya hicimos todas las mv
operaciones de lugar a lugar con el primer comando de la función. En lugar de realizar una acción directa con find
, como una exec
llamada, por ejemplo, la usamos para construir la línea de comandos dinámicamente con -printf
.
%dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
- Después de
find
ubicar los archivos que necesitamos, compila e imprime directamente (la mayoría ) del comando que necesitaremos para procesar su cambio de nombre. La %dir-depth
tachuela al principio de cada línea ayudará a asegurarnos de que no estamos intentando cambiar el nombre de un archivo o directorio en el árbol con un objeto principal que aún no se ha cambiado de nombre. find
utiliza todo tipo de técnicas de optimización para recorrer el árbol del sistema de archivos y no es seguro que devuelva los datos que necesitamos en un orden seguro para las operaciones. Es por eso que a continuación ...
sort -general-numerical -zero-delimited
- Ordenamos todos
find
los resultados en función de %directory-depth
para que se trabajen primero las rutas más cercanas en relación a $ {SRC}. Esto evita posibles errores que involucren mv
archivos en ubicaciones inexistentes y minimiza la necesidad de bucles recursivos. ( de hecho, es posible que tenga dificultades para encontrar un bucle )
sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
- Creo que este es el único bucle en todo el script, y solo recorre el segundo
%Path
impreso para cada cadena en caso de que contenga más de un valor $ {OLD} que pueda necesitar ser reemplazado. Todas las demás soluciones que imaginé implicaban un segundo sed
proceso, y aunque un ciclo corto puede no ser deseable, sin duda es mejor que generar y bifurcar un proceso completo.
- Entonces, básicamente, lo que
sed
hace aquí es buscar $ {sed_sep}, luego, habiéndolo encontrado, lo guarda y todos los caracteres que encuentra hasta que encuentra $ {OLD}, que luego reemplaza con $ {NEW}. Luego regresa a $ {sed_sep} y busca nuevamente $ {OLD}, en caso de que ocurra más de una vez en la cadena. Si no se encuentra, imprime la cadena modificada stdout
(que luego captura de nuevo a continuación) y finaliza el ciclo.
- Esto evita tener que analizar toda la cadena y asegura que la primera mitad de la
mv
cadena de comando, que debe incluir $ {OLD} por supuesto, sí la incluya, y la segunda mitad se modifica tantas veces como sea necesario para borrar el $ {OLD} nombre de mv
la ruta de destino.
sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
- Las dos
-exec
llamadas aquí ocurren sin un segundo fork
. En el primero, como hemos visto, modificamos el mv
comando proporcionado por find
el -printf
comando de función según sea necesario para alterar correctamente todas las referencias de $ {OLD} a $ {NEW}, pero para hacerlo tuvimos que usar algunos puntos de referencia arbitrarios que no deben incluirse en el resultado final. Así que una vezsed
termina todo lo que necesita hacer, le indicamos que borre sus puntos de referencia del búfer de retención antes de pasarlo.
Y ahora estamos de vuelta
read
recibirá un comando que se ve así:
% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000
Va a read
convertirá en ${msg}
como${sh_io}
que se puede examinar a voluntad fuera de la función.
Frio.
-Miguel