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=value
crea una variable (escalar) llamadaidentifier
(si aún no existe) y asigna el valorvalue
lo.
Por ejemplo,G=Man
.
Puede hacer referencia a una variable escalar con$identifier
;
por ejemplo,$G
, o, más seguro, como${identifier}
.
Por ejemplo,$Gage
y$Gilla
puede no estar definido,
pero${G}age
esManage
y${G}illa
esManilla
.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
depattern
constring
.- Si
pattern
comienza con#
,
debe coincidir con el principio del valor expandido delparameter
.
Sipattern
comienza 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
string
de/test
.
Entonces esto reemplaza el final del parámetro con/test
(es decir, añade/test
al 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/
enstring
no se puede interpretar como un delimitador,
y por lo tanto podemos tener unastring
de/test
sin necesidad de escapar del/
. - Si
parameter
es 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.