Looping sobre las inputs de directory en bash y save en una matriz

(Ver actualización al pie de la pregunta).

Esta es una pregunta de seguimiento para "Hacer copys de directory usando find" .

Esta pregunta implicaba manipular un montón de directorys. Esto se volvió demasiado complicado de manejar en un solo command, así que decidí ir con un enfoque que guardaba la list de directorys en una matriz de bash, a pesar de las reservas sobre la portabilidad. Parece que el estándar de shell POSIX (el estándar de shell de Unix, como yo lo entiendo) no tiene matrices.

El file MAKE que estoy usando aparece a continuación. Esto funciona, excepto en el último paso. Un resumen de lo que trato de hacer sigue.

Como se discutió en la pregunta anterior, quiero recorrer el directory x86-headers y recostackr en una matriz bash, una list de sus subdirectorys de nivel superior que contienen el file C/populate.sh (pero también pueden contener otros files). En mi configuration de testing, por ejemplo, solo hay un directory en x86-headers que contiene el file libc/C/populate.sh , es decir, libc .

Luego realizo algunas operaciones en estos subdirectorys. El más importante de estos es que hago una copy temporal de cada directory que se ve como libc.tmp_g4zlb . Es decir, dirname seguido de 'tmp_' seguido de una cadena aleatoria de 5 dígitos.

Entonces, algunas preguntas:

1) Como se discutió en la pregunta anterior, estoy recorriendo el directory 'x86-headers'. Aquí todavía estoy usando find. @Gilles indicó en su respuesta que esto no era ideal. Él podría estar en lo cierto. Problemas con encontrar aquí:

a) Los valores devueltos se parecen a ./libc . No quiero un líder ./ .

b) El command find que estoy usando enumera todos los directorys. Solo quiero considerar aquellos que contienen un file con la ruta relativa de C/populate.sh .

El enfoque que Gilles estaba usando podría ser mejor, pero no lo entiendo. Ver el objective de gilles a continuación. Me gustaría get la list de directorys y savelos en una matriz.

2) El bit con el que estoy teniendo problemas es el último paso, a saber

 echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :$$i'.tmp_'$(RND)))" | \ /usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done \ 

El bit relevante es que estoy tratando de pasar el valor temporal libc.tmp_g4zlb a ccl, que es un comstackdor Common Lisp. Sin la sustitución, se vería como

 echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :libc.tmp_g4zlb))" | \ /usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done \ 

Esto funciona. La versión que usa $$i arriba no. El problema parece ser el líder ./ . Sin eso, debería funcionar. Para el logging, el error que obtengo es

 ? > Error: Too many arguments in call to #<Compiled-function CCL::PARSE-STANDARD-FFI-FILES #x488B8406>: > 3 arguments provided, at most 2 accepted. > While executing: CCL::PARSE-STANDARD-FFI-FILES, in process listener(1). 

Sin embargo, este no es el error que obtengo al pasar ./libc.tmp_g4zlb al comstackdor. Ese error parece

 > Error: Filename "/home/faheem/test/foo/x86-headers/.\\/libc.tmp_g4zlb/C/**/" contains illegal character #\/ > While executing: CCL::%SPLIT-DIR, in process listener(1). 

Entonces, es posible que algo más esté pasando.

3) ¿El enfoque general parece razonable? Por favor, siéntase libre de sugerir posibles mejoras, incluso si se trata de una estrategia completamente diferente.

 #!/usr/bin/make -f # -*- makefile -*- export SHELL=/bin/bash export RND:=$(shell tr -cd a-z0-9 < /dev/urandom | head -c5) export CCL_DEFAULT_DIRECTORY=$(CURDIR) clean: rm -rf x86-headers/libc.tmp* foo: clean PATH=$$PATH:$(CURDIR)/ffigen4/bin; \ echo $$PATH; \ export CURDIR=$(CURDIR); \ echo $$CURDIR; \ array=( $$(cd x86-headers && find . -mindepth 1 -maxdepth 1 -type d) ); \ cd x86-headers && \ for i in "$${array[@]}"; do \ echo $$i; done; \ for i in "$${array[@]}"; do \ mkdir -p $$i."tmp_"$(RND)/C; done; \ for i in "$${array[@]}"; do \ cp -p $$i/C/populate.sh $$i".tmp_"$(RND)/C; done; \ for i in "$${array[@]}"; do \ cd $$i".tmp_"$(RND)/C; ./populate.sh; done; \ for i in "$${array[@]}"; do \ echo $$i'.tmp_'$(RND); done; \ for i in "$${array[@]}"; do \ echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :$$i'.tmp_'$(RND)))" | \ /usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done; \ gilles: cd x86-headers; for x in */C/populate.sh; do \ echo -- "$${x%%/*}$$suffix"; done; \ 

ACTUALIZACIÓN: es posible que la pregunta (o preguntas) se haya perdido en todos los detalles. Entonces, déjame intentar simplificar las cosas. En su respuesta , Gilles escribió

 for x in */C/populate.sh; do mkdir -- "${x%%/*}$suffix" mkdir -- "${x%%/*}$suffix/C" cp -p -- "$x" "./${x%%/*}$suffix/C" done 

Cuando comencé su pregunta, x aquí coincide con los patrones de la forma */C/populate.sh . Además, ${x%%/*} coincide con la primera parte de la cadena, es decir, el nombre del directory de nivel superior. Ahora, algo como

 for x in */C/populate.sh; do myarr[] = "${x%%/*}" done 

crearía una matriz que contiene una list de directorys de nivel superior, que es lo que quiero. Sin embargo, no sé qué syntax usar. Necesito usar un contador que se ejecute en el ciclo, como i=0, 1,... para indexar myarr en el LHS. Si tuviera un código de trabajo como este, me ayudaría a resolver mi problema.

Si desea agregar cosas a una matriz en un bucle, puede usar algo como:

 for x in */C/populate.sh; do myarr=(${myarr[@]} "${x%%/*}") done 

Consulte la documentation de Bash Array para ver muchas cosas que puede hacer con ellos.

Alternativamente, puede usar += para agregar a una matriz (ver aquí ) de esta manera:

  myarr+=("${x%%/*}") 

Algunos comentarios:

Cuando comencé su pregunta, x aquí coincide con los patrones de la forma */C/populate.sh .

Así no es como lo habría explicado. */foo/bar es un patrón glob. Se expande por el shell a una list de todos los files / directorys que coinciden con este patrón. (Pruebe echo */C/populate.sh , verá todas las coincidencias impresas).
El ciclo for itera sobre este set de coincidencias, usando $x como la variable de ciclo.

Además, ${x%%/*} coincide con la primera parte de la cadena, es decir, el nombre del directory de nivel superior.

${x%%/*} no "coincide" con nada. Es una function de manipulación de cadenas que funciona en $x . De los documentos , ${var%%string} elimina la coincidencia más larga de la string desde el final de $var . En este caso, elimina todo del primero / adelante y "devuelve" (se expande a) eso.

Entonces, para romper las tres líneas de código anteriores, lo que sucede es:

  • el shell genera una list de elementos (files o directorys) que coinciden con glob */C/populate.sh .
  • para cada uno de ellos, el cuerpo del bucle se ejecuta con $x establecido en ese elemento
  • ${myarr[@]} expande a la list de cada elemento en la matriz
  • ${x%%/*} expande a $x less todo desde el primero / adelante
  • myarr luego se reconstruye con sus contenidos anteriores más el nuevo elemento despojado.