Me estoy confundiendo un poco sobre cómo manejar los espacios en los nombres de las rutas cuando se devuelven en un bucle for.
Justificación: estoy limpiando los permisos de las carpetas y los archivos que copio desde Windows. La mayoría de los archivos terminan con -rwx------
o -rwxr-xr-x
permisos, así que me gusta hacer " chmod -x *
" y luego " chmod u+x <folders>
", así que intento lo siguiente:
$ alias getdirs='find . -maxdepth 1 -mindepth 1 -type d | cut -c 3-'
$ for i in $(getdirs); do chmod u+x $i; done
que funciona bien, siempre que los directorios no tengan un espacio en el nombre.
Probé diferentes permutaciones de chmod u+x "$i"
y chmod u+x '$i'
similares para obtener el comportamiento que quería, pero fue en vano.
¿Cómo mejorar mi código bash, que funciona con nombres de carpetas que contienen espacio?
El propósito de esto es poder eliminar el bit "exec" de los archivos sin formato (de ahí la chmod -x *
parte) pero luego restaurarlo en los directorios para permitir acceder a ellos ( chmod u+x <dirname>
). De los comentarios y respuestas hasta ahora, creo que probablemente será más fácil hacerlo con el encantamiento adecuado de "encontrar".
Este tipo de cosas pueden ser complicadas en todos los shells de Unix debido a la forma en que el espacio actúa como un separador, ejecutar alias como parte de los scripts de shell hace que las cosas sean aún más interesantes. Probablemente ejecutaría dos pasadas de find para establecer primero los directorios en orden y luego los archivos:
find . -maxdepth 1 -mindepth 1 -type d -exec chmod u+x '{}' \;
find . -maxdepth 1 -mindepth 1 -type f -exec chmod u-x '{}' \;
find . -maxdepth 1 -mindepth 1 -type f -exec chmod u-x '{}' \;
En general, la forma 'adecuada' de analizar la salida de find en un bucle bash es usar un while read
bucle, en lugar de un for
bucle. En bash, los bucles for se dividen usando cualquier espacio en blanco (espacio, tabulador, nueva línea) de forma predeterminada; esto se puede cambiar, pero es más fácil y (en mi opinión) más limpio de usar read
, que lee una línea a la vez de forma predeterminada.
find . -maxdepth 1 -mindepth 1 -type d | while read i; do chmod u+x "$i"; done
Tenga en cuenta que cité "$i"
allí, eso es igual de importante, porque citar variables evita que el shell divida su contenido (es el mismo problema que for
tiene, pero en el otro extremo). También tenga en cuenta que no puede usar comillas simples: '$i'
devolvería un literal $i
, en lugar del contenido de la variable.
Esto aún fallará en los directorios con líneas nuevas en sus nombres. Hay una solución alternativa que involucra find's -print0
, pero solo he visto líneas nuevas en nombres de archivos creados específicamente para probar scripts. No sé si esto funciona con la versión de bash en OSX (tomado de la wiki de greg ):
find . -maxdepth 1 -mindepth 1 -type d -print0 | while IFS= read -r -d '' i; do chmod u+x "$i"; done
Sin embargo, en este caso es más fácil usar globs: un glob que termina en a /
se expandirá solo a directorios, por lo que podría simplemente
chmod u+x */
(esto funcionará perfectamente bien con espacios, saltos de línea, cualquier cosa). De manera más general, para recorrer todos los directorios:
for f in */; do stuff with "$f"; done
Desafortunadamente, no hay forma de seleccionar archivos solo con globs en ninguna versión de bash (esta es una de las razones por las que prefiero zsh, donde los globs son lo suficientemente poderosos como para no tener que molestarse en encontrarlos).
find
es adecuado. No existe ningún escenario en el que analizar la salida de sea más efectivo que o , o, en circunstancias muy limitadas,find
-exec
-execdir
find … -print0 | xargs -0 …
find … -print0
. Además, sostengo que almacenar en caché una lista de nombres de archivo no desinfectados está superando los límites de las secuencias de comandos de una sola línea. El problema de invalidación de caché tampoco es insignificante.shopt -s dotglob
para habilitar la expansión global de nombre punteado.Tengo dos sugerencias:
sed
para poner comillas alrededor de todos los nombres de directorio.xargs
with argument -L 1
. Esto ejecutará un comando en cada línea de stdin, obviando el for
bucle.Pruebe esta canalización:
find . -maxdepth 1 -mindepth 1 -type d | cut -c 3- | sed 's/.*/"&"/' | xargs -L 1 chmod u+x
El quid de la cuestión es que la división de palabras ocurre para la sustitución de comandos , por lo que los nombres con espacios en ellos son indistinguibles de los nombres separados por completo. Globbing no sufre de esta dificultad, por lo que si hay una manera de identificar directorios con un glob y evitar la sustitución de comandos por completo, está libre. Y ahí está:
chmod -x *
chmod u+x */
Pero incluso esto es demasiado trabajo, porque chmod
tiene la X
bandera simbólica (X mayúscula), que aplica el bit ejecutable solo a directorios y archivos que ya son ejecutables. Así que realmente puedes hacer:
chmod -x *
chmod u+X *
O su alias solo está extrayendo el primer bit antes del espacio, o su bucle for solo está leyendo el primer bit
Puede probar esto agregando algunos comentarios de prueba en sus comandos, he limitado el alias para extraer solo el último directorio encontrado para aislar las iteraciones a 1, imprimí lo que el alias cree que está recibiendo y luego repetí el contenido de la variable para asegurar ellos coinciden:
jaravj$ alias getdirs='find . -maxdepth 1 -mindepth 1 -type d | tail -1| cut -c 3-'
jaravj$ getdirs
jaravj$ for i in $(getdirs); do echo "Filename is "$i; chmod u+x $i; done
EDITAR: cambiado do; echo ...
a do echo ...
como estaba impidiendo que la línea se ejecutara
ZZPRODUCT data folder
! Filename is ZZPRODUCT data folder
Si fuera como se esperaba, la salida no habría sido Filename is ZZPRODUCT
como usted indica. Stuffe ha encontrado el problema.getdirs
no lo está, el problema está en el bucle thap, si getdirs
está truncado, el problema está en su alias. Habiendo dicho eso, me gusta la solución de @patrix, ¿la has probado?
mmmmmm
relleno
find
ysed
,awk
etc., muchos menos experimentados los expertos en shell encuentran que las cadenas de comandos de estilo de diagrama de flujo son más legibles y comprensibles.jjarava
mmmmmm
jjarava
kojiro