Nous savons tous que mkfifo
et des pipelines. Le premier crée un nommée Il faut donc choisir un nom, très probablement avec le mot mktemp
et se rappeler plus tard de le dissocier. L'autre crée un tuyau anonyme, sans avoir à se soucier des noms et des suppressions, mais les extrémités du tuyau sont liées aux commandes du pipeline, et il n'est pas vraiment pratique de s'emparer des descripteurs de fichiers et de les utiliser dans le reste du script. Dans un programme compilé, je ferais simplement ret=pipe(filedes)
; dans Bash, il y a exec 5<>file
On pourrait donc s'attendre à quelque chose comme "exec 5<> -"
o "pipe <5 >6"
Existe-t-il quelque chose de ce genre dans Bash ?
Réponses
Trop de publicités?Vous pouvez dissocier un tuyau nommé immédiatement après l'avoir attaché au processus en cours, ce qui donne pratiquement un tuyau anonyme :
# create a temporary named pipe
PIPE=$(mktemp -u)
mkfifo $PIPE
# attach it to file descriptor 3
exec 3<>$PIPE
# unlink the named pipe
rm $PIPE
...
# anything we write to fd 3 can be read back from it
echo 'Hello world!' >&3
head -n1 <&3
...
# close the file descriptor when we are finished (optional)
exec 3>&-
Si vous voulez vraiment éviter les tuyaux nommés (par exemple, le système de fichiers est en lecture seule), votre idée de "s'emparer des descripteurs de fichiers" fonctionne également. Notez que cela est spécifique à Linux en raison de l'utilisation de procfs.
# start a background pipeline with two processes running forever
tail -f /dev/null | tail -f /dev/null &
# save the process ids
PID2=$!
PID1=$(jobs -p %+)
# hijack the pipe's file descriptors using procfs
exec 3>/proc/$PID1/fd/1 4</proc/$PID2/fd/0
# kill the background processes we no longer need
# (using disown suppresses the 'Terminated' message)
disown $PID2
kill $PID1 $PID2
...
# anything we write to fd 3 can be read back from fd 4
echo 'Hello world!' >&3
head -n1 <&4
...
# close the file descriptors when we are finished (optional)
exec 3>&- 4<&-
Bash 4 a coprocessus .
Un coprocessus est exécuté de manière asynchrone dans un sous-shell, comme si la commande avait été terminée par l'opérateur de contrôle '&', avec un tuyau bidirectionnel établi entre le Shell en cours d'exécution et le coprocessus.
Le format d'un coprocessus est le suivant :
coproc [NAME] command [redirections]
Tandis que l'article de @DavidAnderson sur les répondre couvre toutes les bases et offre quelques garanties intéressantes, la chose la plus importante qu'il révèle est qu'il est aussi facile de mettre la main sur un tuyau anonyme que de le faire à l'aide d'un ordinateur. <(:)
tant que vous restez sous Linux.
La réponse la plus courte et la plus simple à votre question est donc la suivante :
exec 5<> <(:)
Sous macOS, cela ne fonctionnera pas, vous devrez alors créer un répertoire temporaire pour y placer le fifo nommé jusqu'à ce que vous ayez redirigé vers lui. Je ne sais pas ce qu'il en est pour les autres systèmes BSD.
En octobre 2012, cette fonctionnalité ne semble toujours pas exister dans Bash, mais coproc peut être utilisé si tout ce dont vous avez besoin des tuyaux non nommés/anonymes est de parler à un processus enfant. Le problème avec coproc à ce stade est qu'apparemment un seul est supporté à la fois. Je n'arrive pas à comprendre pourquoi les coproc ont eu cette limitation. Ils auraient dû être une amélioration du code d'arrière-plan de tâche existant (le & op), mais c'est une question pour les auteurs de bash.
La fonction suivante a été testée en utilisant GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)
. Le système d'exploitation était Ubuntu 18. Cette fonction prend un seul paramètre qui est le descripteur de fichier désiré pour la FIFO anonyme.
MakeFIFO() {
local "MakeFIFO_upper=$(ulimit -n)"
if [[ $# -ne 1 || ${#1} -gt ${#MakeFIFO_upper} || -n ${1%%[0-9]*} || 10#$1 -le 2
|| 10#$1 -ge MakeFIFO_upper ]] || eval ! exec "$1<> " <(:) 2>"/dev/null"; then
echo "$FUNCNAME: $1: Could not create FIFO" >&2
return "1"
fi
}
La fonction suivante a été testée en utilisant GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
. Le système d'exploitation était macOS High Sierra. Cette fonction commence par créer un FIFO nommé dans un répertoire temporaire connu uniquement du processus qui l'a créé . Ensuite, le descripteur de fichier est redirigé vers la FIFO. Enfin, la FIFO est dissociée du nom de fichier en supprimant le répertoire temporaire. Cela rend la FIFO anonyme.
MakeFIFO() {
MakeFIFO.SetStatus() {
return "${1:-$?}"
}
MakeFIFO.CleanUp() {
local "MakeFIFO_status=$?"
rm -rf "${MakeFIFO_directory:-}"
unset "MakeFIFO_directory"
MakeFIFO.SetStatus "$MakeFIFO_status" && true
eval eval "${MakeFIFO_handler:-:}'; true'"
}
local "MakeFIFO_success=false" "MakeFIFO_upper=$(ulimit -n)" "MakeFIFO_file="
MakeFIFO_handler="$(trap -p EXIT)"
MakeFIFO_handler="${MakeFIFO_handler#trap -- }"
MakeFIFO_handler="${MakeFIFO_handler% *}"
trap -- 'MakeFIFO.CleanUp' EXIT
until "$MakeFIFO_success"; do
[[ $# -eq 1 && ${#1} -le ${#MakeFIFO_upper} && -z ${1%%[0-9]*}
&& 10#$1 -gt 2 && 10#$1 -lt MakeFIFO_upper ]] || break
MakeFIFO_directory=$(mktemp -d) 2>"/dev/null" || break
MakeFIFO_file="$MakeFIFO_directory/pipe"
mkfifo -m 600 $MakeFIFO_file 2>"/dev/null" || break
! eval ! exec "$1<> $MakeFIFO_file" 2>"/dev/null" || break
MakeFIFO_success="true"
done
rm -rf "${MakeFIFO_directory:-}"
unset "MakeFIFO_directory"
eval trap -- "$MakeFIFO_handler" EXIT
unset "MakeFIFO_handler"
"$MakeFIFO_success" || { echo "$FUNCNAME: $1: Could not create FIFO" >&2; return "1"; }
}
Les fonctions ci-dessus peuvent être combinées en une seule fonction qui fonctionnera sur les deux systèmes d'exploitation. Vous trouverez ci-dessous un exemple d'une telle fonction. Ici, on tente de créer une FIFO véritablement anonyme. En cas d'échec, une FIFO nommée est créée et convertie en FIFO anonyme.
MakeFIFO() {
MakeFIFO.SetStatus() {
return "${1:-$?}"
}
MakeFIFO.CleanUp() {
local "MakeFIFO_status=$?"
rm -rf "${MakeFIFO_directory:-}"
unset "MakeFIFO_directory"
MakeFIFO.SetStatus "$MakeFIFO_status" && true
eval eval "${MakeFIFO_handler:-:}'; true'"
}
local "MakeFIFO_success=false" "MakeFIFO_upper=$(ulimit -n)" "MakeFIFO_file="
MakeFIFO_handler="$(trap -p EXIT)"
MakeFIFO_handler="${MakeFIFO_handler#trap -- }"
MakeFIFO_handler="${MakeFIFO_handler% *}"
trap -- 'MakeFIFO.CleanUp' EXIT
until "$MakeFIFO_success"; do
[[ $# -eq 1 && ${#1} -le ${#MakeFIFO_upper} && -z ${1%%[0-9]*}
&& 10#$1 -gt 2 && 10#$1 -lt MakeFIFO_upper ]] || break
if eval ! exec "$1<> " <(:) 2>"/dev/null"; then
MakeFIFO_directory=$(mktemp -d) 2>"/dev/null" || break
MakeFIFO_file="$MakeFIFO_directory/pipe"
mkfifo -m 600 $MakeFIFO_file 2>"/dev/null" || break
! eval ! exec "$1<> $MakeFIFO_file" 2>"/dev/null" || break
fi
MakeFIFO_success="true"
done
rm -rf "${MakeFIFO_directory:-}"
unset "MakeFIFO_directory"
eval trap -- "$MakeFIFO_handler" EXIT
unset "MakeFIFO_handler"
"$MakeFIFO_success" || { echo "$FUNCNAME: $1: Could not create FIFO" >&2; return "1"; }
}
Voici un exemple de création d'une FIFO anonyme, puis d'écriture d'un texte dans cette même FIFO.
fd="6"
MakeFIFO "$fd"
echo "Now is the" >&"$fd"
echo "time for all" >&"$fd"
echo "good men" >&"$fd"
Voici un exemple de lecture de l'intégralité du contenu de la FIFO anonyme.
echo "EOF" >&"$fd"
while read -u "$fd" message; do
[[ $message != *EOF ]] || break
echo "$message"
done
Le résultat est le suivant.
Now is the
time for all
good men
La commande ci-dessous ferme la FIFO anonyme.
eval exec "$fd>&-"
Références :
Création d'un tuyau anonyme pour une utilisation ultérieure
Les fichiers se trouvant dans des répertoires accessibles au public sont dangereux
Shell Shell Sécurité
- Réponses précédentes
- Plus de réponses