47 votes

En Bash, comment trouver le plus petit numéro inutilisé descripteur de fichier?

Dans un Bash script, est-il possible d'ouvrir un fichier sur "le plus petit numéro de descripteur de fichier ne sont pas encore en utilisation"?

J'ai regardé autour pour savoir comment faire cela, mais il semble que Bash toujours vous demande d'indiquer le nombre, par exemple comme ceci:

exec 3< /path/to/a/file    # Open file for reading on file descriptor 3.

En revanche, je voudrais être en mesure de faire quelque chose comme

my_file_descriptor=$(open_r /path/to/a/file)

ce qui permettrait d'ouvrir "fichier" pour la lecture sur le plus petit numéro de descripteur de fichier ne sont pas encore en utilisation et d'attribuer ce numéro à la variable "my_file_descriptor'.

77voto

weldabar Points 395

Je sais que ce fil est vieux, mais nous croyons que la meilleure réponse est manquant, et serait utile pour d'autres personnes comme moi qui viennent ici chercher une solution.

Bash et Zsh ont construit dans les façons de trouver inutilisés des descripteurs de fichiers, sans avoir à écrire des scripts. (Je n'ai pas trouvé une telle chose pour le tableau de bord, de sorte que les réponses ci-dessus, peut toujours être utile).

Remarque: ce trouve le plus bas inutilisés descripteur de fichier > 10, pas le plus bas dans l'ensemble.

$ man bash /^REDIRECTION (paragraph 2)
$ man zshmisc /^OPENING FILE DESCRIPTORS

Exemple fonctionne avec bsh et zsh.

Ouvrir un fichier inutilisé descripteur, et affecter le numéro d' $FD:

$ exec {FD}>test.txt
$ echo line 1 >&$FD
$ echo line 2 >&$FD
$ cat test.txt
line 1
line 2
$ echo $FD
10  # this number will vary

Fermer le descripteur de fichier lorsque fait:

$ exec {FD}>&-

La suite montre que le descripteur de fichier est maintenant fermé:

$ echo line 3 >&$FD
bash: $FD: Bad file descriptor
zsh: 10: bad file descriptor

7voto

Basile Starynkevitch Points 67055

Si il est sur Linux, vous pouvez toujours lire l' /proc/self/fd/ annuaire pour trouver les utilisé des descripteurs de fichiers.

5voto

Coren Points 151

J'ai révisé ma réponse originale à cette question et ont maintenant une solution en ligne pour le post original.
La fonction suivante pourrait vivre dans un monde de fichier ou issus du script (par exemple,~/.bashrc):

# Some error code mappings from errno.h
readonly EINVAL=22   # Invalid argument
readonly EMFILE=24   # Too many open files

# Finds the lowest available file descriptor, opens the specified file with the descriptor
# and sets the specified variable's value to the file descriptor.  If no file descriptors
# are available the variable will receive the value -1 and the function will return EMFILE.
#
# Arguments:
#   The file to open (must exist for read operations)
#   The mode to use for opening the file (i.e. 'read', 'overwrite', 'append', 'rw'; default: 'read')
#   The global variable to set with the file descriptor (must be a valid variable name)
function openNextFd {
    if [ $# -lt 1 ]; then
        echo "${FUNCNAME[0]} requires a path to the file you wish to open" >&2
        return $EINVAL
    fi

    local file="$1"
    local mode="$2"
    local var="$3"

    # Validate the file path and accessibility
    if [[ "${mode:='read'}" == 'read' ]]; then
        if ! [ -r "$file" ]; then
            echo "\"$file\" does not exist; cannot open it for read access" >&2
            return $EINVAL
        fi
    elif [[ !(-w "$file") && ((-e "$file") || !(-d $(dirname "$file"))) ]]; then
        echo "Either \"$file\" is not writable (and exists) or the path is invalid" >&2
        return $EINVAL
    fi

    # Translate mode into its redirector (this layer of indirection prevents executing arbitrary code in the eval below)
    case "$mode" in
        'read')
            mode='<'
            ;;
        'overwrite')
            mode='>'
            ;;
        'append')
            mode='>>'
            ;;
        'rw')
            mode='<>'
            ;;
        *)
            echo "${FUNCNAME[0]} does not support the specified file access mode \"$mode\"" >&2
            return $EINVAL
            ;;
    esac

    # Validate the variable name
    if ! [[ "$var" =~ [a-zA-Z_][a-zA-Z0-9_]* ]]; then
        echo "Invalid variable name \"$var\" passed to ${FUNCNAME[0]}" >&2
        return $EINVAL
    fi

    # we'll start with 3 since 0..2 are mapped to standard in, out, and error respectively
    local fd=3
    # we'll get the upperbound from bash's ulimit
    local fd_MAX=$(ulimit -n)
    while [[ $fd -le $fd_MAX && -e /proc/$$/fd/$fd ]]; do
        ((++fd))
    done

    if [ $fd -gt $fd_MAX ]; then
        echo "Could not find available file descriptor" >&2
        $fd=-1
        success=$EMFILE
    else
        eval "exec ${fd}${mode} \"$file\""
        local success=$?
        if ! [ $success ]; then
            echo "Could not open \"$file\" in \"$mode\" mode; error: $success" >&2
            fd=-1
        fi
    fi

    eval "$var=$fd"
    return $success;
}

On pourrait utiliser ce qui précède fonction comme suit pour ouvrir des fichiers d'entrée et de sortie:

openNextFd "path/to/some/file" "read" "inputfile"
# opens 'path/to/some/file' for read access and stores
# the descriptor in 'inputfile'

openNextFd "path/to/other/file" "overwrite" "log"
# truncates 'path/to/other/file', opens it in write mode, and
# stores the descriptor in 'log'

Et on aurait alors utiliser le précédant descripteurs comme d'habitude pour la lecture et l'écriture des données:

read -u $inputFile data
echo "input file contains data \"$data\"" >&$log

2voto

Mikko Östlund Points 689

Dans Basile Starynkevitch, la réponse à cette question, le 29 Novembre 2011, il écrit:

Si il est sur Linux, vous pouvez toujours lire le fichier /proc/self/fd/ annuaire pour trouver les utilisé des descripteurs de fichiers.

Ayant fait plusieurs expériences basées sur la lecture de la fd répertoire, je suis arrivé à la suite du code, comme le "plus proche" de ce que je cherchais. Ce que je cherchais était en fait qu'un coup d'un paquebot, comme

my_file_descriptor=$(open_r /path/to/a/file)

qui aurait trouver le plus bas, inutilisé descripteur de fichier ET ouvrez le fichier ET l'affecter à la variable. Comme on le voit dans le code ci-dessous, en introduisant la fonction "lowest_unused_fd", j'ai au moins obtenir un "deux-liner" (FD=$(lowest_unused_fd) suivie par eval "exec $FD<$FILENAME") pour la tâche. Je n'ai PAS été capable d'écrire une fonction qui fonctionne comme (l'imaginaire) "open_r" ci-dessus. Si quelqu'un sait comment faire, s'il vous plaît pas en avant! Au lieu de cela, j'ai dû diviser la tâche en deux étapes: une étape pour trouver le solde non utilisé des descripteurs de fichiers, et un pas à ouvrir le fichier. À noter également que, pour être en mesure de placer le trouvez pas dans une fonction ("lowest_unused_fd") et avoir son stdout attribué à FD, j'ai dû utiliser "/proc/$$/fd" au lieu de "/proc/self/fd" (comme dans Basile Starynkevitch de la suggestion), depuis bash engendre un shell interne est exécuté pour l'exécution de la fonction.

#!/bin/bash

lowest_unused_fd () {
    local FD=0
    while [ -e /proc/$$/fd/$FD ]; do
        FD=$((FD+1))
    done
    echo $FD
}

FILENAME="/path/to/file"

#  Find the lowest, unused file descriptor
#+ and assign it to FD.
FD=$(lowest_unused_fd)

# Open the file on file descriptor FD.
if ! eval "exec $FD<$FILENAME"; then
    exit 1
fi

# Read all lines from FD.
while read -u $FD a_line; do
    echo "Read \"$a_line\"."
done

# Close FD.
eval "exec $FD<&-"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X