توی این درس یاد میگیری هر برنامهی لینوکسی سه «لوله» دارد: stdin (ورودی، شمارهی ۰)، stdout (خروجی عادی، ۱) و stderr (خطا، ۲). با redirect این لولهها را به فایل یا جای دیگر وصل میکنی: > (نوشتن، رونویسی)، >> (افزودن)، 2> (خطا)، 2>&1 (خطا را همجا با خروجی)، < (ورودی از فایل)، و /dev/null برای دور ریختن. با tee همزمان هم میبینی و هم ذخیره میکنی. تمرین: خروجی موفق و خطای یک دستور را در دو فایل جدا نگه میداری.
مسئله: خروجی روی صفحه گم میشود
Section titled “مسئله: خروجی روی صفحه گم میشود”دستوری که ۱۰۰۰ خط خروجی دارد یا شبانه از cron اجرا میشود، نمیتواند خروجی را «روی صفحه» بگذارد؛ کسی آنجا نیست که بخواند. باید خروجی را به فایل بفرستی. و وقتی خطا و خروجی عادی هر دو روی صفحهاند، جدا کردنشان سخت است: کدام پیام خطاست؟ redirect همین دو مشکل را حل میکند.
تشبیه: لولهکشی ساختمان
Section titled “تشبیه: لولهکشی ساختمان”هر برنامه سه شیر دارد: ورودی (آب تمیز میآید)، خروجی (فاضلاب عادی میرود) و خطا (فاضلاب هشدار جدا میرود). بهطور پیشفرض ورودی از صفحهکلید و هر دو خروجی به صفحه وصلاند. redirect یعنی لوله را جابهجا کنی: خروجی به یک مخزن (فایل)، خطا به مخزن دیگر یا به چاه (/dev/null).
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: دو جریان، یک صفحه
Section titled “مثال ۱: دو جریان، یک صفحه”دستوری که هم موفق میشود هم خطا میدهد: ls با دو مسیر، یکی موجود و یکی ناموجود:
ls /etc/hostname /no-such-file/etc/hostnamels: cannot access '/no-such-file': No such file or directoryدو خط چاپ شد: /etc/hostname (stdout) و پیام خطا (stderr). هر دو روی صفحه آمدند و از هم تشخیصپذیر نیستند. با redirect جدایشان میکنیم.
مثال ۲: خروجی به فایل، > و >>
Section titled “مثال ۲: خروجی به فایل، > و >>”> خروجی را به فایل میفرستد؛ اگر فایل بود رونویسی میکند:
ls /etc/hostname /no-such-file > out.txtecho "--- محتوای out.txt:"cat out.txtls: cannot access '/no-such-file': No such file or directory--- محتوای out.txt:/etc/hostnameفقط stdout در out.txt رفت؛ پیام خطا هنوز روی صفحه آمد (stderr جداست). نکته: > فایل را از اول مینویسد:
echo "خط اول" > notes.txtecho "خط دوم" > notes.txtcat notes.txtecho "--- با >> (افزودن):"echo "خط سوم" >> notes.txtecho "خط چهارم" >> notes.txtcat notes.txtخط دوم--- با >> (افزودن):خط دومخط سومخط چهارم> دوم محتوای قبلی را پاک کرد و فقط «خط دوم» ماند. >> به انتها اضافه میکند. برای لاگ و گزارش معمولاً >>؛ برای ساختن فایل تازه >. و > اگر فایل نباشد میسازد.
مثال ۳: خطا به فایل، 2>
Section titled “مثال ۳: خطا به فایل، 2>”عدد ۲ شمارهی stderr است: 2> فقط خطاها را redirect میکند:
ls /etc/hostname /no-such-file 2> errors.txtecho "--- errors.txt:"cat errors.txt/etc/hostname--- errors.txt:ls: cannot access '/no-such-file': No such file or directoryحالا stdout روی صفحه آمد و خطا در فایل رفت؛ دقیقاً برعکس مثال قبل. هر دو را همزمان جدا کن:
ls /etc/hostname /no-such-file > ok.txt 2> err.txtecho "ok.txt: $(cat ok.txt)"echo "err.txt: $(cat err.txt)"ok.txt: /etc/hostnameerr.txt: ls: cannot access '/no-such-file': No such file or directoryهیچچیز روی صفحه نیامد: خروجی در ok.txt و خطا در err.txt. این همان تمرین درس است. (اگر > را بدون عدد بنویسی، یعنی 1>.)
دستور cmd 2> err.txt چه چیزی را در err.txt میریزد؟
عدد ۲ شمارهی stderr است؛ خروجی عادی (۱) روی صفحه میماند.
مثال ۴: هر دو در یک فایل، 2>&1 و ترتیب
Section titled “مثال ۴: هر دو در یک فایل، 2>&1 و ترتیب”برای ریختن هر دو جریان در یک فایل: > فایل 2>&1 (یعنی «stdout به فایل؛ stderr را هم به همانجا که stdout میرود»):
ls /etc/hostname /no-such-file > all.txt 2>&1echo "--- all.txt:"cat all.txt--- all.txt:ls: cannot access '/no-such-file': No such file or directory/etc/hostnameمیانبر bash برای همین: &> فایل. ترتیب مهم است: redirect ها از چپ به راست اعمال میشوند:
echo "--- ترتیب درست: > f 2>&1"ls /etc/hostname /no-such-file > right.txt 2>&1echo "در فایل: $(wc -l < right.txt) خط"echo "--- ترتیب اشتباه: 2>&1 > f"ls /etc/hostname /no-such-file 2>&1 > wrong.txtecho "در فایل: $(wc -l < wrong.txt) خط (خطا روی صفحه ماند)"--- ترتیب درست: > f 2>&1در فایل: 2 خط--- ترتیب اشتباه: 2>&1 > fls: cannot access '/no-such-file': No such file or directoryدر فایل: 1 خط (خطا روی صفحه ماند)در ترتیب اشتباه، اول 2>&1 یعنی «stderr را به جایی که الآن stdout هست (صفحه) وصل کن»؛ بعد stdout به فایل میرود و stderr روی صفحه میماند. (در خروجی بالا خط خطا بالای «در فایل» دیده شد.) قاعده: اول فایل، بعد 2>&1.
مثال ۵: دور ریختن، /dev/null
Section titled “مثال ۵: دور ریختن، /dev/null”/dev/null سیاهچاله است: هر چه بنویسی گم میشود (درس ساختار پوشهها):
echo "--- خطا را ساکت کن:"ls /etc/hostname /no-such-file 2>/dev/nullecho "--- خروجی عادی را ساکت کن (فقط خطا):"ls /etc/hostname /no-such-file >/dev/nullecho "--- همهچیز ساکت (فقط کد خروج مهم است):"ls /etc/hostname /no-such-file >/dev/null 2>&1; echo "کد خروج: $?"--- خطا را ساکت کن:/etc/hostname--- خروجی عادی را ساکت کن (فقط خطا):ls: cannot access '/no-such-file': No such file or directory--- همهچیز ساکت (فقط کد خروج مهم است):کد خروج: 2الگوی > /dev/null 2>&1 برای «بیصدا اجرا کن» در اسکریپتها و cron بسیار رایج است (اکنون میدانی یعنی چه). احتیاط: 2>/dev/null خطاها را هم پنهان میکند؛ فقط وقتی مطمئنی نیازی به دیدنشان نیست.
مثال ۶: ورودی از فایل، <
Section titled “مثال ۶: ورودی از فایل، <”< محتوای فایل را به stdin برنامه میدهد (برنامه فکر میکند از صفحهکلید میخواند):
printf 'banana\napple\ncherry\n' > fruits.txtecho "--- wc با نام فایل:"wc -l fruits.txtecho "--- wc با ورودی redirect:"wc -l < fruits.txtecho "--- sort از stdin:"sort < fruits.txt--- wc با نام فایل:3 fruits.txt--- wc با ورودی redirect:3--- sort از stdin:applebananacherryفرق دو حالت wc: با اسم فایل، wc خودش فایل را باز میکند و اسمش را چاپ میکند؛ با < فقط یک جریان میبیند و اسمی نمیداند (فقط عدد). برای نتیجهی تمیز در اسکریپت wc -l < فایل مفید است. همچنین here-document (<<) یک متن چندخطی را مستقیم به stdin میدهد:
cat > config.txt <<'LXEOF'name=webport=8080debug=falseLXEOFcat config.txtecho "--- here-string (<<<):"wc -w <<< "یک رشتهی کوتاه برای شمارش"name=webport=8080debug=false--- here-string (<<<):5<<'LXEOF' تا خطی که فقط LXEOF باشد را بهعنوان ورودی میخواند (علامتهای quote دور اسم مانع جایگزینی متغیرها میشود). و <<< یک رشتهی تکخطی را به stdin میدهد. این روش نوشتن فایلهای چندخطی در اسکریپت و درسهای بعد است.
مثال ۷: همزمان ببین و ذخیره کن، tee
Section titled “مثال ۷: همزمان ببین و ذخیره کن، tee”tee ورودیاش را هم روی صفحه چاپ میکند و هم در فایل مینویسد (مثل سهراهی). معمولاً با | میآید (درس بعد):
echo "گزارش اول" | tee log.txtecho "گزارش دوم" | tee -a log.txtecho "--- محتوای log.txt:"cat log.txtگزارش اولگزارش دوم--- محتوای log.txt:گزارش اولگزارش دومبدون -a فایل را رونویسی میکند و با -a (append) اضافه میکند. کاربرد مهم: نوشتن در فایلهای root. sudo echo x > /etc/file کار نمیکند، چون sudo فقط به echo داده میشود و redirect را shell (کاربر عادی) انجام میدهد:
sudo echo "سلام" > /etc/lx-test.txtecho "--- با sudo tee:"echo "سلام" | sudo tee /etc/lx-test.txtcat /etc/lx-test.txtsudo rm /etc/lx-test.txtbash: /etc/lx-test.txt: Permission denied--- با sudo tee:سلامسلامخطای اول Permission denied از redirect است (shell با اختیار تو آن را انجام میدهد)؛ با | sudo tee خود tee با root اجرا میشود و میتواند بنویسد.
مثال ۸: خالی کردن فایل و کپی استریمها
Section titled “مثال ۸: خالی کردن فایل و کپی استریمها”echo "مقداری محتوا" > data.txtls -l data.txt | awk '{print "قبل:", $5, "بایت"}'> data.txtls -l data.txt | awk '{print "بعد از > data.txt:", $5, "بایت"}'echo "یک بار دیگر" > data.txt: > data.txtls -l data.txt | awk '{print "بعد از : > data.txt:", $5, "بایت"}'قبل: 24 بایتبعد از > data.txt: 0 بایتبعد از : > data.txt: 0 بایت> فایل بهتنهایی فایل را به صفر بایت میرساند (بدون پاک کردن آن؛ دسترسیها و مالک میماند). : یک دستور «هیچکاری نکن» است و : > فایل همان را در اسکریپتها امنتر مینویسد. برای لاگ بزرگی که برنامه هنوز باز دارد، همین خالی کردن (نه rm) فضا را آزاد میکند.
مثال ۹: یک نگاه به فایلهای توصیفگر
Section titled “مثال ۹: یک نگاه به فایلهای توصیفگر”هر پروسه لیستی از file descriptor ها دارد: ۰، ۱، ۲ و… که هر کدام به یک فایل/دستگاه/لوله اشاره میکند. میتوانی ببینی redirect چه کرده:
echo "--- بدون redirect (لولهی ترمینال ما):"ls -l /proc/self/fd | awk 'NR>1 && $9 ~ /^[0-2]$/ {print $9, $10, $11}' | sed 's#/dev/pts/[0-9]*#/dev/pts/N#; s#pipe:\[[0-9]*\]#pipe:[N]#' | head -3echo "--- با > ورودی و خروجی را به فایل ببر:"ls -l /proc/self/fd > fds.txt 2> /dev/null < /etc/hostnameawk 'NR>1 && $9 ~ /^[0-2]$/ {print $9, $10, $11}' fds.txt | sed 's#/home/ali/#~/#'--- بدون redirect (لولهی ترمینال ما):0 -> /dev/null1 -> pipe:[N]2 -> pipe:[N]--- با > ورودی و خروجی را به فایل ببر:0 -> /etc/hostname1 -> ~/fds.txt2 -> /dev/nullدر حالت دوم fd شمارهی ۰ (stdin) به /etc/hostname، شمارهی ۱ (stdout) به fds.txt و شمارهی ۲ (stderr) به /dev/null اشاره میکند. شل قبل از اجرای ls این اتصالها را درست کرد؛ خود ls فقط از fd ۱ مینویسد و نمیداند کجا میرود.
مثال ۱۰: ترکیبهای رایج
Section titled “مثال ۱۰: ترکیبهای رایج”echo "--- ۱) لاگگیری از اجرای یک کار (هر دو جریان در فایل، پسزمینه):"( sleep 0.2; echo "کار تمام شد"; ls /nope ) > job.log 2>&1 &waitcat job.logecho "--- ۲) فقط خطاها را ببین و به فایل بفرست، خروجی عادی را بیندازد دور:"ls /etc/hostname /nope > /dev/null 2> only-errors.txtcat only-errors.txtecho "--- ۳) stderr را به pipe بفرست (|&):"ls /etc/hostname /nope |& wc -l--- ۱) لاگگیری از اجرای یک کار (هر دو جریان در فایل، پسزمینه):کار تمام شدls: cannot access '/nope': No such file or directory--- ۲) فقط خطاها را ببین و به فایل بفرست، خروجی عادی را بیندازد دور:ls: cannot access '/nope': No such file or directory--- ۳) stderr را به pipe بفرست (|&):2|& میانبر 2>&1 | است: هم stdout و هم stderr را به دستور بعدی میدهد. و در مثال اول & در انتها دستور را در پسزمینه میبرد و wait منتظرش میماند.
پشت پرده: shell قبل از exec، دوبارهسیمکشی میکند
Section titled “پشت پرده: shell قبل از exec، دوبارهسیمکشی میکند”وقتی ls > out.txt را میزنی، shell (۱) یک فرزند میسازد (fork)، (۲) در فرزند، out.txt را باز میکند و fd شمارهی ۱ را به آن وصل میکند (dup2)، (۳) و بعد ls را اجرا میکند (exec). ls از fd شمارهی ۱ مینویسد و اصلاً نمیداند فایل است یا صفحه یا لوله. به همین دلیل همهی برنامهها بدون هیچ تغییری با redirect و pipe کار میکنند، و برای همین ترتیب redirect ها مهم است: shell آنها را بهترتیب چپ به راست اجرا میکند.
2>&1 یعنی «fd شمارهی ۲ را کپی کن از fd شمارهی ۱ در همین لحظه». اگر قبل از آن > فایل آمده باشد، ۱ به فایل اشاره میکند و ۲ هم به فایل؛ اگر بعد از آن بیاید، ۲ به مقصد قبلی ۱ (صفحه) اشاره میکرد و بعد ۱ جابهجا میشود. همین است که > f 2>&1 و 2>&1 > f فرق دارند.
یک خطر مهم: > فایل را قبل از اجرای دستور خالی میکند. پس sort file.txt > file.txt فایل را خالی میکند و بعد sort روی فایل خالی کار میکند: داده از بین میرود. برای این کار موقت بنویس (sort file.txt > tmp && mv tmp file.txt) یا sort -o file.txt file.txt.
جدولهای مرجع
Section titled “جدولهای مرجع”| نماد | کار |
|---|---|
> f |
stdout به فایل (رونویسی) |
>> f |
stdout به انتهای فایل |
2> f |
stderr به فایل |
2>> f |
stderr به انتهای فایل |
> f 2>&1 یا &> f |
هر دو به فایل |
2>&1 |
stderr را به جای stdout بفرست |
< f |
stdin از فایل |
<< 'EOF' |
here-document (چندخطی) |
<<< "متن" |
here-string |
| tee f / tee -a f |
هم نمایش، هم ذخیره (یا افزودن) |
|& |
stdout و stderr را به pipe بده |
/dev/null |
سیاهچاله |
| شماره | نام | پیشفرض |
|---|---|---|
| 0 | stdin | صفحهکلید |
| 1 | stdout | صفحه |
| 2 | stderr | صفحه |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) > بهجای >> و از دست دادن داده
Section titled “۱) > بهجای >> و از دست دادن داده”مثال ۲: «خط دوم» محتوای قبلی را پاک کرد. راهحل: برای افزودن >>؛ و برای محافظت، در shell تعاملی set -o noclobber را بگذار تا > روی فایل موجود خطا بدهد:
echo "مهم" > precious.txtset -o noclobberecho "رونویسی" > precious.txtecho "--- با >> (افزودن) مشکلی نیست:"echo "اضافه" >> precious.txtcat precious.txtset +o noclobberbash: precious.txt: cannot overwrite existing file--- با >> (افزودن) مشکلی نیست:مهماضافه(با >| هم میتوان آگاهانه رونویسی را اجبار کرد.)
۲) ترتیب اشتباه 2>&1 > f
Section titled “۲) ترتیب اشتباه 2>&1 > f”مثال ۴. راهحل: اول فایل، بعد 2>&1.
۳) خواندن و نوشتن یک فایل در یک دستور
Section titled “۳) خواندن و نوشتن یک فایل در یک دستور”printf 'c\na\nb\n' > list.txtsort list.txt > list.txtecho "طول list.txt بعد از sort list.txt > list.txt: $(wc -c < list.txt) بایت"طول list.txt بعد از sort list.txt > list.txt: 0 بایتshell فایل را قبل از sort خالی کرد و همهی داده رفت. راهحل: sort list.txt -o list.txt یا فایل موقت.
۴) sudo cmd > فایل-root
Section titled “۴) sudo cmd > فایل-root”مثال ۷: redirect با دسترسی تو انجام میشود، نه root. راهحل: cmd | sudo tee فایل.
۵) پنهان کردن خطا با 2>/dev/null و بعد تعجب از اینکه «کار نکرد»
Section titled “۵) پنهان کردن خطا با 2>/dev/null و بعد تعجب از اینکه «کار نکرد»”cp /no/such/file /tmp/x 2>/dev/null; echo "کد خروج: $?"کد خروج: 1دستور بیصدا شکست خورد ولی هیچ پیامی ندیدی؛ فقط کد خروج غیرصفر نشانه بود. راهحل: خطا را در یک فایل لاگ بریز (2>> errors.log) نه جای سیاهچاله، و کد خروج را چک کن.
خروجی date را در فایل today.txt بنویس، بعد خروجی whoami را به انتهای همان فایل اضافه کن و محتوا را نشان بده.
دیدن جواب
date +%Y-%m-%d > today.txtwhoami >> today.txtcat today.txt2026-10-03aliتمرین اصلی: خروجی موفق و خطای یک دستور را در دو فایل جدا ذخیره کن. دستور ls /etc/hostname /etc/os-release /no-file را طوری اجرا کن که خروجی در ok.txt و خطا در err.txt برود، بعد تعداد خط هر فایل را بنویس.
دیدن جواب
ls /etc/hostname /etc/os-release /no-file > ok.txt 2> err.txtecho "ok.txt ($(wc -l < ok.txt) خط):"; cat ok.txtecho "err.txt ($(wc -l < err.txt) خط):"; cat err.txtok.txt (2 خط):/etc/hostname/etc/os-releaseerr.txt (1 خط):ls: cannot access '/no-file': No such file or directoryیک تابع run-logged بنویس که یک دستور بگیرد و اجرا کند، خروجی عادی را در run.out، خطاها را در run.err ذخیره کند، همزمان هر دو را روی صفحه با پیشوند [OUT] یا [ERR] نشان بدهد (راهنما: process substitution یا tee)، و در پایان کد خروج را چاپ کند. روی ls /etc/hostname /nope امتحان کن.
دیدن جواب
run-logged() { "$@" > >(tee run.out | sed 's/^/[OUT] /') 2> >(tee run.err | sed 's/^/[ERR] /' >&2) local rc=$? sleep 0.3 echo "کد خروج: $rc"}run-logged ls /etc/hostname /nope 2>&1echo "--- run.out:"; cat run.outecho "--- run.err:"; cat run.err[ERR] ls: cannot access '/nope': No such file or directory[OUT] /etc/hostnameکد خروج: 2--- run.out:/etc/hostname--- run.err:ls: cannot access '/nope': No such file or directory>(...) (process substitution) یک «فایل» میسازد که ورودیاش به یک دستور میرود؛ اینجا tee هم ذخیره میکند و هم sed پیشوند میگذارد.
آزمونک
Section titled “آزمونک”شمارهی stdout و stderr؟
0 stdin است.
تفاوت > و >>؟
با > محتوای قبلی از دست میرود.
کدام دستور هر دو جریان را در یک فایل میریزد؟
ترتیب مهم است: اول فایل، بعد 2>&1.
cmd >/dev/null 2>&1 چه میکند؟
در cron و اسکریپت برای اجرای بیصدا رایج است.
چرا sudo echo x > /etc/file کار نمیکند؟
تنها echo با sudo اجرا میشود.
چرا sort f > f خطرناک است؟
از -o یا فایل موقت استفاده کن.
جمعبندی
Section titled “جمعبندی”- هر برنامه سه جریان دارد: stdin (0)، stdout (1)، stderr (2)؛ shell قبل از اجرا آنها را وصل میکند.
>رونویسی،>>افزودن،2>خطا،&>یا> f 2>&1هر دو (اول فایل، بعد2>&1)،<ورودی از فایل،<<here-document./dev/nullسیاهچاله؛>/dev/null 2>&1یعنی ساکت.teeهم نشان میدهد هم ذخیره میکند؛| sudo teeبرای نوشتن در فایلهای root.>فایل را قبل از اجرای دستور خالی میکند؛ هرگزcmd f > fننویس.set -o noclobberمحافظ است.- خطاها را جدا (
2> errors.log) نگه دار تا پیدا کردنشان آسان باشد؛ بیدلیل2>/dev/nullنگذار.
| دستور | کاری که میکند |
|---|---|
cmd > out.txt | خروجی به فایل (رونویسی) |
cmd >> out.txt | خروجی به انتهای فایل |
cmd 2> err.txt | فقط خطا به فایل |
cmd > out.txt 2> err.txt | خروجی و خطا در دو فایل جدا |
cmd > all.txt 2>&1 (یا &> all.txt) | هر دو در یک فایل |
cmd > /dev/null 2>&1 | اجرای بیصدا |
cmd < in.txt | ورودی از فایل |
cmd | tee log.txt | هم نمایش، هم ذخیره |
cmd | tee -a log.txt | افزودن به انتهای فایل |
echo x | sudo tee /etc/f | نوشتن در فایل root |
> file (یا : > file) | خالی کردن فایل |
set -o noclobber | جلوگیری از رونویسی تصادفی با > |