A tubulação para saída de loop impede a modificação da variável local

10

Estou tentando escrever uma função bash simples que utiliza, como argumentos, vários arquivos e / ou diretórios. Deve:

  1. Qualifique totalmente os nomes dos arquivos.
  2. Classifique-os.
  3. Remover duplicatas.
  4. Imprima tudo o que realmente existe.
  5. Retorna o número de arquivos inexistentes.

Eu tenho um script que quase faz o que eu quero, mas cai na classificação. O valor de retorno do script, como está, está correto, mas a saída não é (não classificada e duplicado). Se eu descomentar a instrução | sort -u conforme indicado, a saída está correta, mas o valor de retorno é sempre 0 .

N.B. Soluções mais simples para resolver o problema são bem-vindas, mas a questão é realmente sobre por que isso está ocorrendo no código que tenho. Por que adicionar o pipe aparentemente interrompe o script ao incrementar a variável r ?

Aqui está o script:

function uniqfile
{
    local r=0 

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

    done #| sort -u    ## remove that comment

    return $r
}
    
por tjm 30.09.2011 / 10:30

2 respostas

15

Essa é uma armadilha bem conhecida, devido a esse recurso :

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

para que as variáveis modificadas sejam locais para o subshell e não sejam visíveis novamente no pai.

Para evitar isso, reformule seu código para evitar o pipeline, com uma substituição de processo:

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

    done > >(sort -u)
    
por 30.09.2011 / 10:50
3

O | sort -u força o bit anterior a ser executado em um subprocesso (o bash precisa de um 'STDOUT' para redirecionar para o sort 'STDIN'. (A Internet parece pensar ksh e bash lidam com este caso de forma ligeiramente diferente .. o primeiro ou último comando na seqüência de pipe é colocado em uma subcamada?)

Este tópico passa por um problema semelhante e tem uma solução simples no final: link

trecho
    #!/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"
    
por 30.09.2011 / 10:47