BASH: Actualizar parametros del for, se puede?

Buenas noches, estoy haciendo una practica de sistemas operativos, y me ha surgido una duda bastante existencial...

El caso es que en la primera practica, tenia un for parametro , que cogia directamente los parametros que le pasaban al programa, pero bien, si intentaba hacer shift ; shift dentro del for, no lo hacia, porque (supongo yo), que debe ser para no alterar la cadena de entrada al for que estaba establecida desde un principio. Pero bueno, esto he podido arreglarlo de una forma un poco guarrilla.

Pero es que en la segunda, tengo que recorrer los directorios de forma recursiva, y para ello, tambien tengo un for, este si que lo pego.

for nombre in $dir
do
IFS='
'
for arxiu in `ls $nombre | tail +2`
do
IFS=` `
if [ -f $nombre"/"$arxiu ]; then
echo "Arxiu: $nombre/$arxiu"
numf=$((numf+1))
elif [ -d $nombre"/"$arxiu ]; then
echo "Directori $nombre/$arxiu"
let numd++
dir="$dir $nombre/$arxiu" #esta linea modifica dir
fi
done
done


lo que tiene que hacer es recorrer recursivamente un directorio entrado por el usuario, el path, se encuentra en dir, entonces, lo que he pensado es, entrar en el mismo, y si encuentro un directorio, añadirselo a dir para entrar dentro luego, pero no modifica la variable. Tengo que hacer algo aparte, o simplemente no es posible??

Saludos!!
Tres apuntes (suponiendo que tengas que hacerlo así y no usando find):

a) No uses `ls $nosequé` usa la expansión de parámetros de bash, que para eso está. Tu ejemplo falla cuando los nombres de los ficheros contienen espacios.
b) El problema te está pidiendo a gritos que crees una función que se llame a sí misma, es decir, una función recursiva.
c) Existe un problema añadido: los enlaces simbólicos. Como te los encuentres puedes entrar en un bucle infinito. Así que recuerda tratarlos.
Buenas noches, gracias por tu ayuda auxiliar, he resuelto esto con una funcion recursiva, pero ahora tengo un problemilla. Respecto a los consejos que me has dado, el ls noseque, debe hacerse asi, puesto que es el script que tenemos que modificar (eso no lo podemos tocar), al igual que los espacios en los directorios.. Con respecto a lo de los enlaces, solo llamare a la funcion recursiva en caso de que sea un directorio, y un enlace lo trata como un fichero. Pero gracias por el apunte, ahora me surge la siguiente duda.

Al hacer la llamada recursiva, deja de explorar la carpeta que esta explorando actualmente y entra en la funcion con la nueva ruta (la primera carpeta que encuentra), asi va avanzando carpetas, hasta que llega un punto en que hay una que no contiene mas carpetas, y todo lo que tiene son archivos. Ahora bien, en este momento, el programa acaba, no sigue explorando las carpetas que habia empezado a explorar. Por ejemplo, si la ruta es /home/usuario i dentro existen las carpetas documentos/apuntes/2, documentos/apuntes/1, documentos/juegos.....

la unica carpeta que explorara sera documentos (hasta que encuentre 1) (si es que lo hace alfabeticamente), y de apuntes/2 i juegos pasara... por no mencionar que hubiera otra carpeta a la misma altura que documentos, pues tambien pasaria de ella.

Os adjunto el trozo de codigo de la funcion, haber si veis algo.

function funcio()
{
nombre=$1
IFS='
'

for arxiu in `ls $nombre | tail +2`
do
IFS=` `
if [ -f $nombre"/"$arxiu ]; then
echo "Arxiu: $nombre/$arxiu"
numf=$((numf+1))
elif [ -d $nombre"/"$arxiu ]; then
echo "Directori $nombre/$arxiu"
let numd++
funcio "$nombre/$arxiu" #aqui es donde llamo a la funcion otra vez
fi
done
}


Saludos!!
La idea es esa... pero es tarde y no tengo tiempo de confrontar código. Esto funciona:

#!/bin/sh

function explora() {

  for i in $1/*; do
     if [ -d "$i" ]; then
       echo "[d] $i... Explorando..."
       explora $i
#  else
#    echo "$i"
     fi
  done
}

explora $1


He comentado esas dos líneas para que no me muestre ficheros, sólo directorios. El programa funciona:

$ ./chorradita.sh . | wc -l
41
$ tree -d . | wc -l
44


Las tres líneas de diferencia son el propio directorio ".", una línea en blanco y el total de directorios, que no muestra el script y sí tree.

Haz los cambios oportunos para que aparezca "ls", aunque yo no me explico cómo se puede ser tan chapuzas.
Acabo de hacerle una práctica similar a un compañero... los problemas son 1) los profesores son saben bash 2) los profesores no saben bash 3) goto 1

Salu2.Ferdy
xDDD pues tienes razon Ferdy, al menos esto es una chapuza del 15!!! He conseguido acabar este problema, (fallo tonto, como no), la variable nombre es global, si la defino como local dentro de la funcion, va perfecto el programa.

Ahora me encuentro haciendo otro, pero tengo un problema grande con las tuberias... (Con lo bien que se le dan a auxiliar) jajaja. Estas en todos los hilos referentes a ellas tio!!

Bien, lo que tengo es un fichero temporal obtenido a partir de ls -lR $HOME > temporal, lo que hago es mirar los que son directorios y pertenecen a un determinado usuario, como quiero obtener el tamaño de cada directorio, pongo el cut -f5, pero me falta una ultima tuberia para poder sumar todos los tamaños, ya que me pide numero total de directorios (lo tengo), i luego sumar el tamaño de todos ellos. Para ello hago grep '^d' | cut -f3 | grep $usuario | cut -f5 |...

Me han estado hablando de xargs, pero me he mirado el man y no entiendo su funcionamiento...

Saludos!!
A ver qué te parece la solución:
bash$ echo $(($(ls -lR | awk '/^d/ && $3 == USUARIO {printf $5 "+"}' USUARIO="josem")0))
dios dios dios..... :O :O :O :O :O :O :O no entiendo nada... jajaja es que de awk apenas hemos visto nada tio, lo he probado, sí, pero no me entero de nada. Esto calcula el valor de todas las carpetas o tambien archivos, es que lo que debo hacer es solo las carpetas, he visto que todas ocupan 4096, (4kb) i he pensado en 4096*num_carpetas, porque con esto, creo que calculas todo no??

Saludos!!
No sé sobre el sistema de ficheros a fondo, así que no sé decirte si todos ocupan 4096. De todos modos:

$  ls -ld /dev
drwxr-xr-x  14 root root 34504 2005-03-14 08:43 /dev


Este parece que no. Para entender lo que he hecho: esto es como operar. haz las cosas de adentro a afuera:

a) awk determina si la línea empieza por "d" y si el usuario (el campo $3) es igual al que le dices. Si se cumplen ambas condiciones imprime el tamaño (campo $5). Además manipulo el formato de salida para que en vez de aparecer un número por línea, aparezcan todos los números seguidos por el signo "+". Esto generaría algo así:

4+6+6+7+

Esta línea la puedes ver escribiendo lo que hay en el parántesis más interno.

Todo bien excepto por el hecho de que me sobra un +. Así que lo que hago es añadir un 0 al final:

4+6+6+7+0

b) Sumo la ristra de número, que en bash se puede hacer con el operador $((...)):

$ echo $((1+1))
2


EDITO: Para responder a Ferdy...

Pues sí tienes razón... Pero estos son universitarios... A mí el catedrático de turno me puso un ejercicio de bash en las oposiciones a Secundaria, que era para echarse a llorar. Vamos que se había leído un howto y no se le había ocurrido otra cosa más estúpida.
Buenas otra vez, retomo el hilo, para eta vez, sí!, hablar del awk, el caso es que tengo que calcular el funcionamiento de una red, pero tengo un pequeño problemilla.. al programa de entrada, llega archivos del tipo appLoad*.sout, donde * es la carga aplicada a la red. Estos archivos son del tipo:

30 343,64 305,52 131,92 944,00 0,15
30 338,98 331,69 147,18 1116,00 0,13
30 272,48 366,75 155,50 1156,00 0,14
30 255,75 386,13 155,97 1247,00 0,16
30 296,74 428,50 190,66 1384,00 0,14
30 287,36 442,75 195,47 1318,00 0,13
30 274,73 476,79 183,32 1498,00 0,13
30 279,33 500,11 172,80 1232,00 0,13
30 354,61 395,63 164,51 1692,00 0,12
30 317,46 404,19 160,48 1010,00 0,12
30 315,46 448,56 183,73 1605,00 0,12
30 318,47 481,11 189,60 1327,00 0,12
30 325,73 378,90 140,82 991,00 0,12
30 257,07 387,60 150,89 1130,00 0,15
30 259,74 384,65 146,70 1169,00 0,14
30 178,89 400,33 151,79 1228,00 0,19
30 292,28 407,45 163,83 1252,94 0,14


Este, por ejemplo sería el appLoad30.sout, el * coincide con la 1a columna. Lo que tengo que hacer ahora es calcular la media de todas las columnas, aparte de la penultima, que tengo que calcular el maximo y guardarlo en un fichero llamado exercici.sout, si fuera un fichero guay, hasta aqui perfecto, pero ahora tengo un problema...

Los appLoad*.sout, pueden ser varios, entonces, lo que tengo que hacer es calcular lo mismo para todos los ficheros, la media de cada columna, y escribirlo en exercici.sout, pero no hay manera... Lo que me hace es calcular la media de todos los ficheros columna a columna, y me crea un monton de lineas... lo que tengo hasta ahora es esto, (solo lo he echo para una columna).

le paso todos los appLoad*.sout, incremento s mas el valor de la columna, i imprimo s dividido por el numero de lineas cuando acabo con los ficheros, y ello lo guardo en exercici.sout, antes tenía:

awk '{s += $1} END {print s/NR}' appLoad*.sout > exercici.sout

pero lo que hace es guardarme el ultimo valor de la media, lo que me gustaría es que hiciera esto exactamente, pero para cada fichero por separado.

Saludos!! I gracias!


Saludos!!
Si lo que has puesto te funciona, has probado con:

for i in appLoad*.sout ; do
    awk '{s += $1} END {print s/NR}' ${i} > ${i}-out
done


? O algo similar vaya...

Cheers,
Ferdy
buenas gente, ferdy, muchas gracias tio, ahora va de cine, lo he hecho para todas las columnas, y me imprime al final de fichero sin borrar lo anteriormente escrito. Todo de puda madre, pero me falla la division!! jajaja lo que parecia mas facil... Pues si, al hacer este cacho {s += $1} END {print s/NR}, en algunas columnas me imprime bien, pero en otras, como en la ultima, por ejemplo mal, tengo que decir que en la ultima tengo cifras del tipo 0,98 0,86 0,99 ... y me escribe 0 y ya esta, existe otra forma de dividir??

EDITO: Lo he estado mirando y el fallo viene dado porque solo suma enteros, y al final divide por el NR y por eso da decimales, el fallo esta en la suma, como puedo sumar decimales tios??

Saludos y mil gracias!!
Con los decimales me ayudo Ferdy cuando estaba haciendo un script para el torrentflux (para que mostrara los datos en un terminal). Es una movida de mucho cuidado:
echo 7/3 | bc -l | sed 's:\([0-9]*.[0-9]\{2\}\)[0-9]*:\1:g'

En el primer cacho metes la operación que quieras hacer, en este caso 7/3. Luego con bc -l calculas el resultado, con nada mas y nada menos que 30 decimales. Al final con sed le dices que reduzca los decimales a 2. Si quieres que salgan mas decimales solo tienes que cambiar el '2' del sed por la cantidad de decimales que quieras.
Por ejemplo
[.-josu@arcueid josu $ -.] echo 1.12+1.25 | bc -l | sed 's:\([0-9]*.[0-9]\{3\}\)[0-9]*:\1:g'
2.37


Agur y espero que te ayude en algo
buenas otra vez, pues he probado con awk'{r=`echo "$2 + $r" | tr "," "."| bc -l` ; ...}' archivo, pero nada de nada, al ser el bc una orden que no pertenece a awk, me da error al ejecutarlo... No hay funciones o algo así para lograrlo? Por mas que busco no encuentro nada...

Saludos!!
Siento no estar al tanto de nada pero llevo un par de días sin internet en casa...

Por lo que he leído así por encima tu problema está en que debes cambiar comas por puntos: lo puedes hacer con el propio awk: Hay una función que se llama "sub" para cambiar caracteres en una cadena. Por ejemplo:

bash$ echo "15,10" | awk '{sub(/,/,".",$0);print $0}'
15.10


Con lo que te han dicho y esto, ya tienes resuelto el problema.
de vicio auxiliar, como siempre!! Solo hacer un apunte por si a alguien le interesa tambien, necesitaba cambiar todas las comas por puntos, lo que no mencione, es que tengo varios numeros decimales en una misma linea, con sub, solamente cambiaba el primero, si a alguien le interesa cambiar toda la linea debe hacerlo con gsub. gsub( "," , "." , $0)

Saludos y gracias de nuevo!!
/me aún está aprendiendo awk... debería ponerse las pilas
DeadLock escribió:de vicio auxiliar, como siempre!! Solo hacer un apunte por si a alguien le interesa tambien, necesitaba cambiar todas las comas por puntos, lo que no mencione, es que tengo varios numeros decimales en una misma linea, con sub, solamente cambiaba el primero, si a alguien le interesa cambiar toda la linea debe hacerlo con gsub. gsub( "," , "." , $0)

Saludos y gracias de nuevo!!


Si el problema es cambiar las líneas, con tr en bash se puede hacer (tanto sustituir como eliminar)

Ferdy escribió:/me aún está aprendiendo awk... debería ponerse las pilas


[qmparto] venga hombre, no me creo que awk suponga ningún esfuerzo pal nivel que hay :D ( controlando expresiones regulares y sabiendo un poco de C está chupado )

1 Salu2
Manual de awk en castellano. Explica todo lo quie hay que saber...

Sigo sin internet... :(
Estoy esperando http://www.oreilly.com/catalog/sed2/ . Que me vendrá bien para perfeccionar mi sed y de paso aprender awk :P

anyway, gracias por el enlace.

Saludos.Ferdy
auxiliar escribió:Manual de awk en castellano. Explica todo lo quie hay que saber...

Sigo sin internet... :(


Jeje, curiosamente de ahí saqué la funcion gsub() ;)

OT: Que te pasa que estas preocupado por internet compi??

Saludos!!
Ferdy escribió:Estoy esperando http://www.oreilly.com/catalog/sed2/ . Que me vendrá bien para perfeccionar mi sed y de paso aprender awk


¿Le echaste un ojo a mi tutorial de sed? Sinceramente, con sed no creo que se puedan hacer muchas más cosas de las que exponía en él. Lo usé hasta de calculadora...

DeaDlocK escribió:Que te pasa que estas preocupado por internet compi??


En absoluto preocupado, simplemente que me he quedado sin internet en casa por problemas con las puñeteras ISP (presumiblemente hasta mañana) y me conecto en el trabajo cuando puedo (es decir, casi nunca).
¿Le echaste un ojo a mi tutorial de sed? Sinceramente, con sed no creo que se puedan hacer muchas más cosas de las que exponía en él. Lo usé hasta de calculadora...


Si lo hice si, por aquí anda. No es que necesite muchas más cosas de sed; sin embargo trae tambien expresiones regulares, ejemplos y el tema de awk. Me viene muy bien para afianzar+aprender awk :P

Saludos.Ferdy
22 respuestas