Il piping per l'output del loop impedisce la modifica delle variabili locali


11

Sto cercando di scrivere una semplice funzione bash che prende, come argomenti, un numero di file e / o directory. Dovrebbe:

  1. Qualificare completamente i nomi dei file.
  2. Ordinali.
  3. Rimuovi duplicati.
  4. Stampa tutto ciò che esiste realmente.
  5. Restituisce il numero di file inesistenti.

Ho una sceneggiatura che fa quasi quello che voglio, ma cade nell'ordinamento. Il valore restituito dello script così com'è è corretto, ma l'output non lo è (non ordinato e duplicato). Se decomprimo l' | sort -uistruzione come indicato, l'output è corretto ma il valore restituito è sempre 0.

NB Le soluzioni più semplici per risolvere il problema sono benvenute, ma la domanda riguarda davvero il motivo per cui ciò accade nel codice che ho. Cioè, perché l'aggiunta della pipe sembra fermare lo script incrementando la variabile r?

Ecco la sceneggiatura:

function uniqfile
{
    local r=0 

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

    done #| sort -u    ## remove that comment

    return $r
}

Solo una piccola osservazione. È possibile ridurre for arg in "$@"a for arg. "If 'in WORDS ...;' non è presente, quindi si presuppone che "in" $ @ "". - aiuto per
arte

Risposte:


15

Questa è una trappola per bash ben nota, grazie a questa caratteristica :

Ogni comando in una pipeline viene eseguito come un processo separato (cioè in una subshell).

in modo che le variabili modificate siano locali nella subshell e non siano visibili una volta nel padre.

Per evitare ciò, riformulare il codice per evitare la pipeline, con una sostituzione del processo:

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

    done > >(sort -u)

Grazie. È fantastico. Mi chiedo se potresti dirmi il nome del >(..command..)costrutto. Io penso che io so come funziona, ma sento che dovrei fare qualche ulteriore lettura.
tjm

2
@tjm: si chiama sostituzione del processo
enzotib

La sostituzione del processo in Bash ha molte forme: tldp.org/LDP/abs/html/process-sub.html
slm

La sostituzione del processo è una forma di comunicazione tra processi che consente all'input o all'output di un comando di apparire come file. Il comando viene sostituito in linea, dove normalmente si verifica un nome di file , dalla shell dei comandi. Ciò consente ai programmi che normalmente accettano solo file di leggere o scrivere direttamente su un altro programma.
nobar

3

Le | sort -uforze del bit precedente (così l'intero ciclo for) per l'esecuzione in un sotto-processo (bash ha bisogno di un 'STDOUT' per reindirizzare in sort'STDIN'. (Internet sembra pensare kshe bashgestire questo caso leggermente diverso .. prima o l'ultima comando nella sequenza di pipe viene inserito in una subshell?)

Questo thread supera un problema simile e alla fine ha una soluzione chiara : http://ubuntuforums.org/showthread.php?t=312017

estratto
    #!/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"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.