نوشته شده به وسیله Stéphane Chazelas، و بازنگری توسط نگارنده این سند
هر فرمان انتظار دارد که ابتدا سه توصیفگر فایل در دسترس باشد. نخست، fd 0 (ورودی استاندارد، stdin)، برای خواندن است. دو توصیفگر دیگر (fd 1، یا stdout و fd 2، یا stderr) برای نوشتن هستند.
با هر فرمان یک stdin، stdout، و یک stderr مرتبط وجود دارد. ls 2>&1 یعنی متصل شدن موقت stderr فرمان ls به همان «منبعی» که stdout پوسته است.
طبق قرار داد، یک فرمان ورودیاش را از fd شماره 0 (stdin) میخواند، خروجی عادیاش را در fd شماره 1 (stdout)، و خروجی خطا را در fd شماره 2 (stderr) مینویسد. اگر یکی از این سه fd باز نباشد، ممکن است شما با مشکلات مواجه بشوید:
bash$ cat /etc/passwd >&- cat: standard output: Bad file descriptor
برای مثال، وقتی xterm اجرا میشود، ابتدا خودش را مقداردهی میکند. قبل از اجرای پوسته کاربر، xterm سه بار دستگاه ترمینال (/dev/pts/<n> یا چیزی مشابه آن) را باز میکند.
در این نقطه، Bash این سه توصیفگر فایل را به ارث میبرد، و هر فرمان (پردازش فرزند) اجرا شده به وسیله Bash به نوبت خود آنها را به ارث میبرد، مگر موقعی که شما فرمان را تغییر مسیر بدهید. تغییر مسیر یعنی دوباره تخصیص دادن یکی از توصیفگرهای فایل به یک فایل دیگر (یا لوله، یا هر چیز مجاز). توصیفگرهای فایل میتوانند به طور محلی (برای یک فرمان، یک گروه فرمان، یک پوسته فرعی، یک حلقه while یا for یا if یا case...)، یا به طور سراسری برای بقیه پوسته (با استفاده از exec) دوباره تخصیص داده شوند.
ls > /dev/null یعنی اجرای ls در حالیکه fd 1 آن به /dev/null متصل شده باشد.
bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 363 bozo 0u CHR 136,1 3 /dev/pts/1 bash 363 bozo 1u CHR 136,1 3 /dev/pts/1 bash 363 bozo 2u CHR 136,1 3 /dev/pts/1 bash$ exec 2> /dev/null bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 371 bozo 0u CHR 136,1 3 /dev/pts/1 bash 371 bozo 1u CHR 136,1 3 /dev/pts/1 bash 371 bozo 2w CHR 1,3 120 /dev/null bash$ bash -c 'lsof -a -p $$ -d0,1,2' | cat COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 379 root 0u CHR 136,1 3 /dev/pts/1 lsof 379 root 1w FIFO 0,0 7118 pipe lsof 379 root 2u CHR 136,1 3 /dev/pts/1 bash$ echo "$(bash -c 'lsof -a -p $$ -d0,1,2' 2>&1)" COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 426 root 0u CHR 136,1 3 /dev/pts/1 lsof 426 root 1w FIFO 0,0 7520 pipe lsof 426 root 2w FIFO 0,0 7520 pipe
این برای انواع مختلف تغییر مسیر کار میکند.
تمرین: اسکریپت زیر را تجزیه و تحلیل نمایید.
#! /usr/bin/env bash mkfifo /tmp/fifo1 /tmp/fifo2 while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 & exec 7> /tmp/fifo1 exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&7) exec 3>&1 ( ( ( while read a; do echo "FIFO2: $a"; done < /tmp/fifo2 | tee /dev/stderr \ | tee /dev/fd/4 | tee /dev/fd/5 | tee /dev/fd/6 >&7 & exec 3> /tmp/fifo2 echo 1st, to stdout sleep 1 echo 2nd, to stderr >&2 sleep 1 echo 3rd, to fd 3 >&3 sleep 1 echo 4th, to fd 4 >&4 sleep 1 echo 5th, to fd 5 >&5 sleep 1 echo 6th, through a pipe | sed 's/.*/PIPE: &, to fd 5/' >&5 sleep 1 echo 7th, to fd 6 >&6 sleep 1 echo 8th, to fd 7 >&7 sleep 1 echo 9th, to fd 8 >&8 ) 4>&1 >&3 3>&- | while read a; do echo "FD4: $a"; done 1>&3 5>&- 6>&- ) 5>&1 >&3 | while read a; do echo "FD5: $a"; done 1>&3 6>&- ) 6>&1 >&3 | while read a; do echo "FD6: $a"; done 3>&- rm -f /tmp/fifo1 /tmp/fifo2 #برای هر فرمان و هر پوسته فرعی، معین کنید که fd به چه اشاره میکند. #خوش باشید! exit 0