it-swarm.it

Quando useresti un descrittore di file aggiuntivo?

So che puoi creare un descrittore di file e reindirizzare l'output su di esso. per esempio.

exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.

Ma puoi fare la stessa cosa senza il descrittore di file:

FILE=/tmp/foo
echo a > "$FILE"

Sto cercando un buon esempio di quando dovresti usare un descrittore di file aggiuntivo.

80
dogbane

La maggior parte dei comandi ha un singolo canale di input (input standard, descrittore di file 0) e un singolo canale di output (output standard, descrittore di file 1) oppure operano su più file che si aprono da soli (quindi si passa loro un nome di file). (Questo è in aggiunta all'errore standard (fd 2), che di solito filtra fino all'utente). Tuttavia, a volte è conveniente avere un comando che agisce come filtro da più fonti o da più target. Ad esempio, ecco un semplice script che separa le righe dispari in un file da quelle pari

while IFS= read -r line; do
  printf '%s\n' "$line"
  if IFS= read -r line; then printf '%s\n' "$line" >&3; fi
done >odd.txt 3>even.txt

Supponiamo ora di voler applicare un filtro diverso alle linee dei numeri dispari e alle linee dei numeri pari (ma non rimetterli insieme, sarebbe un problema diverso, non realizzabile dalla Shell in generale). In Shell, è possibile reindirizzare l'output standard di un comando solo a un altro comando; per reindirizzare un altro descrittore di file, devi prima reindirizzarlo a fd 1.

{ while … done | odd-filter >filtered-odd.txt; } 3>&1 | even-filter >filtered-even.txt

Un altro caso d'uso più semplice è filtrando l'output dell'errore di un comando .

exec M>&N reindirizza un descrittore di file a un altro per il resto dello script (o fino a quando un altro comando di questo tipo non modifica nuovamente i descrittori di file). C'è qualche sovrapposizione di funzionalità tra exec M>&N e somecommand M>&N. Il modulo exec è più potente in quanto non deve essere nidificato:

exec 8<&0 9>&1
exec >output12
command1
exec <input23
command2
exec >&9
command3
exec <&8

Altri esempi che potrebbero essere di interesse:

E per ancora più esempi:

Post scriptum Questa è una domanda sorprendente proveniente dall'autore del post più votato sul sito che utilizza il reindirizzamento tramite fd !

Ecco un esempio dell'uso di FD extra come controllo della chattiness degli script bash:

#!/bin/bash

log() {
    echo $* >&3
}
info() {
    echo $* >&4
}
err() {
    echo $* >&2
}
debug() {
    echo $* >&5
}

VERBOSE=1

while [[ $# -gt 0 ]]; do
    ARG=$1
    shift
    case $ARG in
        "-vv")
            VERBOSE=3
        ;;
        "-v")
            VERBOSE=2
        ;;
        "-q")
            VERBOSE=0
        ;;
        # More flags
        *)
        echo -n
        # Linear args
        ;;
    esac
done

for i in 1 2 3; do
    fd=$(expr 2 + $i)
    if [[ $VERBOSE -ge $i ]]; then
        eval "exec $fd>&1"
    else
        eval "exec $fd> /dev/null"
    fi
done

err "This will _always_ show up."
log "This is normally displayed, but can be prevented with -q"
info "This will only show up if -v is passed"
debug "This will show up for -vv"
14
Fordi

Nel contesto di named pipe (fifos) l'uso di un descrittore di file aggiuntivo può abilitare un comportamento di piping senza blocco.

(
rm -f fifo
mkfifo fifo
exec 3<fifo   # open fifo for reading
trap "exit" 1 2 3 15
exec cat fifo | nl
) &
bpid=$!

(
exec 3>fifo  # open fifo for writing
trap "exit" 1 2 3 15
while true;
do
    echo "blah" > fifo
done
)
#kill -TERM $bpid

Vedi: Named Pipe si chiude prematuramente nello script?

8
chad

Un descrittore di file aggiuntivo è utile quando si desidera catturare lo stdout in una variabile ma si desidera comunque scrivere sullo schermo, ad esempio nell'interfaccia utente di uno script bash

arg1 string to echo 
arg2 flag 0,1 print or not print to 3rd fd stdout descriptor   
function ecko3 {  
if [ "$2" -eq 1 ]; then 
    exec 3>$(tty) 
    echo -en "$1" | tee >(cat - >&3)
    exec 3>&- 
else 
    echo -en "$1"  
fi 
}

Ecco un altro scenario quando si utilizza un descrittore di file aggiuntivo sembra appropriato (in Bash):

Sicurezza della password dello script Shell dei parametri della riga di comando

env -i bash --norc   # clean up environment
set +o history
read -s -p "Enter your password: " passwd
exec 3<<<"$passwd"
mycommand <&3  # cat /dev/stdin in mycommand
3
bernard

Esempio: utilizzo di flock per forzare l'esecuzione in serie degli script con blocchi dei file

Un esempio è quello di utilizzare il blocco dei file per forzare l'esecuzione degli script a livello di sistema in serie. Ciò è utile se non si desidera che due script dello stesso tipo operino sugli stessi file. Altrimenti, i due script interferirebbero tra loro e probabilmente con i dati corrotti.

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#open file descriptor 3 for writing
exec 3> /tmp/file.lock

#create an exclusive lock on the file using file descriptor 3
#exit if lock could not be obtained
flock -n 3

#execute serial code

#remove the file while the lock is still obtained
rm -f /tmp/file.lock

#close the open file handle which releases the file lock and disk space
exec 3>&-

Utilizzare il gregge in modo funzionale definendo il blocco e lo sblocco

Puoi anche avvolgere questa logica di blocco/sblocco in funzioni riutilizzabili. Il seguente trap Shell builtin rilascerà automaticamente il blocco del file alla chiusura dello script (errore o esito positivo). trap aiuta a ripulire i blocchi dei file. Il sentiero /tmp/file.lock dovrebbe essere un percorso codificato in modo che più script possano provare a bloccarlo.

# obtain a file lock and automatically unlock it when the script exits
function lock() {
  exec 3> /tmp/file.lock
  flock -n 3 && trap unlock EXIT
}

# release the file lock so another program can obtain the lock
function unlock() {
  # only delete if the file descriptor 3 is open
  if { >&3 ; } &> /dev/null; then
    rm -f /tmp/file.lock
  fi
  #close the file handle which releases the file lock
  exec 3>&-
}

La logica unlock sopra è quella di eliminare il file prima del rilascio del blocco. In questo modo pulisce il file di blocco. Poiché il file è stato eliminato, un'altra istanza di questo programma è in grado di ottenere il blocco dei file.

Utilizzo delle funzioni di blocco e sblocco negli script

Puoi usarlo nei tuoi script come nell'esempio seguente.

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#try to lock (else exit because of non-zero exit code)
lock

#system-wide serial locked code

unlock

#non-serial code

Se vuoi che il tuo codice aspetti fino a quando non è in grado di bloccare, puoi regolare lo script come:

set -e

#wait for lock to be successfully obtained
while ! lock 2> /dev/null; do
  sleep .1
done

#system-wide serial locked code

unlock

#non-serial code
1
Sam Gleske

Come esempio concreto, ho appena scritto uno script che necessita delle informazioni di temporizzazione da un sottocomando. L'uso di un descrittore di file aggiuntivo mi ha permesso di catturare lo stderr del comando time senza interrompere lo stdout o lo stderr del sottocomando.

(time ls -9 2>&3) 3>&2 2> time.txt

Ciò che fa è puntare _ lo stderr di ls su fd 3, puntare fd 3 sullo stderr dello script e puntare lo stderr di time su un file. Quando viene eseguito lo script, i suoi stdout e stderr sono gli stessi dei sottocomandi, che possono essere reindirizzati come al solito. Solo l'output di time viene reindirizzato al file.

$ echo '(time ls my-example-script.sh missing-file 2>&3) 3>&2 2> time.txt' > my-example-script.sh
$ chmod +x my-example-script.sh 
$ ./my-example-script.sh 
ls: missing-file: No such file or directory
my-example-script.sh
$ ./my-example-script.sh > /dev/null
ls: missing-file: No such file or directory
$ ./my-example-script.sh 2> /dev/null
my-example-script.sh
$ cat time.txt

real    0m0.002s
user    0m0.001s
sys 0m0.001s
1
Ben Blank