La tubería para la salida de bucle evita la modificación de la variable local

11

Estoy tratando de escribir una función bash simple que tome, como argumentos, varios archivos y / o directorios. Debería:

  1. Califique completamente los nombres de archivo.
  2. Clasifícalos.
  3. Eliminar duplicados.
  4. Imprime todo lo que realmente existe.
  5. Devuelve el número de archivos inexistentes.

Tengo un script que casi hace lo que quiero, pero se cae en la clasificación. El valor de retorno del script tal como está es correcto, pero el resultado no lo está (sin clasificar y duplicados). Si descomento la | sort -udeclaración como se indica, la salida es correcta pero el valor de retorno es siempre 0.

NB Las soluciones más simples para resolver el problema son bienvenidas, pero la pregunta es realmente por qué ocurre esto en el código que tengo. Es decir, ¿por qué agregar la tubería aparentemente detiene la secuencia de comandos que incrementa la variable r?

Aquí está el guión:

function uniqfile
{
    local r=0 

    for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done #| sort -u    ## remove that comment

    return $r
}
tjm
fuente
Solo una pequeña observación. Puedes reducir for arg in "$@"a for arg. "Si 'en PALABRAS ...;' no está presente, entonces se asume 'en "$ @"' ". - ayuda para
manatwork

Respuestas:

15

Este es un error conocido de bash, debido a esta característica :

Cada comando en una tubería se ejecuta como un proceso separado (es decir, en una subshell).

para que las variables modificadas sean locales a la subshell y no sean visibles una vez en el padre

Para evitar eso, reformule su código para evitar la canalización, con una sustitución de proceso:

 for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done > >(sort -u)
enzotib
fuente
Gracias. Eso es genial. Me pregunto si podrías decirme el nombre de la >(..command..)construcción. Yo creo que sé cómo funciona, pero siento que debería leer un poco más lejos.
tjm
2
@tjm: se llama sustitución de proceso
enzotib
La sustitución de procesos en Bash tiene muchas formas: tldp.org/LDP/abs/html/process-sub.html
slm
La sustitución de procesos es una forma de comunicación entre procesos que permite que la entrada o salida de un comando aparezca como un archivo. El comando se sustituye en línea, donde normalmente se produciría un nombre de archivo , por el shell del comando. Esto permite que los programas que normalmente solo aceptan archivos lean o escriban directamente en otro programa.
nobar
3

Esto | sort -uobliga a que el bit anterior (por lo tanto, el ciclo for for) se ejecute en un subproceso (bash necesita un 'STDOUT' para redirigirse al sort'STDIN'. (Internet parece pensar kshy bashmanejar este caso de manera ligeramente diferente ... primero o último comando en la secuencia de la tubería se pone en una subshell?)

Este hilo pasa por un problema similar y tiene una solución ordenada al final: http://ubuntuforums.org/showthread.php?t=312017

extracto
    #!/bin/bash
    exec 3< <(du | sort -n)  

    n=0
    while read size dir; do
      [ $size -gt 1000 ] && ((n++))
    done <&3
    exec 3<&-

    echo "Found $n too big files"
PT
fuente