Entonces, hacer algo como esto en bash y la mayoría de los otros shells no funcionarán para crear múltiples subdirectorios o archivos dentro de subdirectorios...
mkdir */test
touch */hello.txt
Por supuesto, hay muchas formas de hacer esto, mi preferida es usar find cuando sea posible en lugar de usar un for loop, principalmente por legibilidad.
Pero mi pregunta es, ¿por qué lo anterior no funciona?
Por lo que entiendo, es porque la ruta/archivo de destino completo no existe, pero seguramente eso es algo bueno si estoy tratando de mkdir o touch . Siempre he seguido adelante y nunca lo cuestioné realmente.
Pero, ¿alguien tiene una explicación decente para esto que me ayude a entender de una vez por todas?
Respuesta aceptada:
El punto que puede estar pasando por alto, con el que muchas personas tienen problemas,
especialmente si tienen experiencia con otros sistemas operativos antes de llegar a *nix
, es que, en muchos otros sistemas operativos, los comodines en el línea de comando normalmente se pasan al comando para procesar como crea conveniente. Por ejemplo, en el símbolo del sistema de Windows,
rename *.jpeg *.jpg
Mientras que, en *nix, para simplificar el trabajo
de los programadores de los comandos individuales (por ejemplo, mv ), los comodines
(cuando no están entrecomillados ni escapados) son manejados por el shell,
de una manera que es independiente de la interfaz y la funcionalidad
del comando para el que los comodines son argumentos.
La mayoría de los programas que toman nombres de archivos como argumentos en la línea de comandos esperan que esos archivos existan
(sí, mkdir y touch son excepciones a esa regla,
al igual que mkfifo , mknod y, en parte, cp , ln , mv y rename;
y probablemente haya otros),
por lo que realmente no tiene sentido que el shell expanda los comodines
a nombres de archivos que no existen.
Y para el caparazón (y con eso me refiero a cada shell –
Bourne, bash, csh, fish, ksh, zsh, etc...) para manejar las excepciones de manera diferente
probablemente sería demasiado complicado.
Dicho esto, hay un par de maneras de obtener un resultado como el que desea.
Si sabe a qué se va a expandir el comodín,
y no es largo, puede generarlo con la expansión de llaves:
touch {red,orange,yellow,green,blue,indigo,violet}/rgb.txt
Una solución más general:
sh -c 'for arg do mkdir -- "$arg"/test; done' -- *
Gilles me ayudó a encontrar otra forma
de hacerlo en bash.
benbradley=(*)
mkdir "${benbradley[@]/%//test}"
Obviamente benbradley es solo un identificador aquí; puede usar cualquier nombre
(por ejemplo, cualquier letra).
Me tomó un par de intentos hacerlo bien, así que déjame desglosarlo:
identifier=valuecrea una variable (escalar) llamadaidentifier(si aún no existe) y asigna el valorvaluelo.
Por ejemplo,G=Man.
Puede hacer referencia a una variable escalar con$identifier;
por ejemplo,$G, o, más seguro, como${identifier}.
Por ejemplo,$Gagey$Gillapuede no estar definido,
pero${G}ageesManagey${G}illaesManilla.identifier=(value1 value2 … )crea una variable de matriz llamadaidentifier(si aún no existe) y le asigna los valores enumerados.
Por ejemplo,
spectrum=(red orange yellow green blue indigo violet)
o
all_text_files=(*.txt)
$name hará referencia al primer elemento (y por lo tanto es bastante inútil).
Puede hacer referencia a un elemento arbitrario como ${name[subscript]};
por ejemplo, ${spectrum[0]} es red y ${spectrum[4]} es blue .
Y puedes usar ${name[@]} y ${name[*]} para hacer referencia a toda la matriz.
Entonces, aquí está en pedazos:
" ${ benbradley[@] / % / /test } " ${parameter/pattern/string}expande${parameter}y reemplaza la coincidencia más larga
depatternconstring.- Si
patterncomienza con#,
debe coincidir con el principio del valor expandido delparameter.
Sipatterncomienza con%,
debe coincidir al final del valor expandido delparameter.
En otras palabras,
%(the-rest_of_the_pattern)
actúa como
(the-rest_of_the_regex)$
(sí, eso parece un poco al revés).
Así que un pattern eso es solo un % es como una expresión regular que es solo un $ –
coincide con el final de la cadena de entrada (espacio de búsqueda).
- Y estoy usando una
stringde/test.
Entonces esto reemplaza el final del parámetro con/test(es decir, añade/testal parámetro). - Otra cosa sobre
${parameter/pattern/string}eso no es intuitivo es que siempre termina con un}.
No es necesario y no puede , termina con un tercer/.
Por lo tanto, un/enstringno se puede interpretar como un delimitador,
y por lo tanto podemos tener unastringde/testsin necesidad de escapar del/. - Si
parameteres una variable de matriz
subíndice con@o*,
la operación de sustitución se aplica a cada miembro de la matriz por turnos. - Cuando hace referencia a una matriz como
${name[@]}(en lugar de${name[*]}) y coloque los resultados entre comillas dobles,
preserva la integridad de los elementos de la matriz
(es decir, conserva los espacios y otros caracteres especiales en ellos)
sin combinar los elementos separados en una palabra larga.