ابتدا، بیایید برخی موضوعات زمینهای را بازبینی کنیم. موقعی که یک پردازش میخواهد پردازش دیگری را اجرا کند، یک فرزند fork()(منشعب) میکند، و پردازش فرزند یکی از فراخوانهای سیستمی خانواده exec* (مانندexecve())، را با دادن نام یا مسیر فایل برنامه پردازش جدید، نام پردازش جدید، لیست شناسهها برای پردازش جدید، و در بعضی حالتها، مجموعهای از متغیرهای محیط، فراخوانی میکند. از این قرار:
/* C */ execlp("ls", "ls", "-l", "dir1", "dir2", (char *) NULL);
محدودیتی(به طور معمول) برای تعداد شناسههایی که به این طریق میتواند عبور داده شود، وجود ندارد، اما در اکثر سیستمها، حدی برای اندازه کل لیست، وجوددارد. برای جزئیات بیشتر، http://www.in-ulm.de/~mascheck/various/argmax/ را ملاحظه کنید.
اگر شما در یک فراخوانی منفرد برنامه، سعی کنید (مثلاً) نامفایلهای بسیار زیادی را عبور بدهید، با موردی مشابه این مواجه میشوید:
$ grep foo /usr/include/sys/*.h bash: /usr/bin/grep: Arg list too long
ترفندهای گوناگونی وجود دارد که میتوانستید برای غلبه براین مورد به صورت تک موردی(فاقد عمومیت) به کار ببرید(اول تعویض دایرکتوری به /usr/include/sys، و سپس استفاده از grep foo *.h برای کوتاه نمودن طول هر نام فایل...)، اما اگر راهکار کاملا نیرومندی لازم داشته باشید چطور؟
بعضی اشخاص در اینجا دوست دارند xargs را به کار ببرند، اما این مورد مسائل خطیری دارد. این فرمان با کاراکترهای فضای سفید و نقلقول در ورودیاش به عنوان جداکننده کلمات رفتار میکند، که آن را برای مدیریت صحیح نامفایلها، نالایق میسازد. (برای تشریح مطلب در این خصوص، صفحه کاربرد find را ببینید.)
اگر عمل بازگشتی مورد قبول است، میتوانید به طور مستقیم find را استفاده کنید:
find /usr/include/sys -name '*.h' -exec grep foo /dev/null {} +
اگر بازگشت قابل قبول نیست اما شما find گنو را دارید، میتوانید این جایگزین غیرقابلحمل را به کار ببرید:
GNUfind /usr/include/sys -name '*.h' -maxdepth 1 -exec grep foo /dev/null {} +
(به یاد بیاورید که اگر grep بیش از یک نام فایل برای پردازش دریافت کند، تنها نامفایلها را چاپ خواهد کرد. پس، ما برای تضمین آنکه همواره حتی اگر -exec فقط یک نام به آن عبور دهد، حداقل دو نامفایل دارد، /dev/null را به عنوان نام فایل به آن عبور میدهیم.)
عمومیترین پیشنهاد استفاده از آرایه Bash و حلقهای برای پردازش در قطعات بزرگ است:
# Bash files=(/usr/include/*.h /usr/include/sys/*.h) for ((i=0; i<${#files[*]}; i+=100)); do grep foo "${files[@]:i:100}" /dev/null done
اینحا، ما پردازش یکصد عنصر در هر نوبت را انتخاب کردهایم، البته این اختیاری است، و شما میتوانید به نسبت پیش بینی شده برای اندازه هر عنصر، در مقایسه با مقدار ARG_MAX برنامه getconf سیستم مقصد، به مقدار بیشتر یا کمتر تنظیم کنید. اگر میخواهید تصوری به دست آورید، میتوانیدبا استفاده از ARG_MAX و اندازه بزرگترین عنصر محاسبه کنید، اما بازهم باید «ضرایب جبرانی» برای اندازه محیط و غیره را در نظر بگیرید. آسانتر است فقط یک مقدار محافظهکارانه را به امید آنکه بهترین مقدار باشد انتخاب نمود.
پرسش و پاسخ 95 (آخرین ویرایش 2010-01-06 14:23:30 توسط GreyCat)
متأسفانه، تجزیه خروجی فرمان df واقعاً معتبرترین روش تعیین پر شدن کامل دیسک در اکثر سیستمعاملها میباشد. به هر حال، لطفاً توجه نمایید که این کم ضررترین پاسخ است، نه بهترین جواب. تجزیه خروجی هر ابزار گزارش خط فرمانی هرگز خوشآیند نمیباشد. مقصود از این FAQ کوششی برای تشریح تمام مشکلات شناخته شده رویاروی این راهکار، و عبور موقت از آنها میباشد.
نخست، بزرگترین مشکل با df آن است که در تمام سیستمعاملها به صورت یکسان کار نمیکند. یونیکس به طور عمده به دو خانواده تقسیم میشود-- System V و BSD. در سیستمهای خانواده BSD(در این وضعیت، شامل لینوکس)، df یک گزارش قابل خواندن انسانی ارائه میکند:
~$ df Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda2 8230432 3894324 3918020 50% / tmpfs 253952 8 253944 1% /lib/init/rw udev 10240 44 10196 1% /dev tmpfs 253952 0 253952 0% /dev/shm
در حالیکه، در سیستمهای همخانواده با System-V، خروجی به طور کامل متفاوت است:
$ df /net/appl/clin (svr1:/dsk/2/clin/pa1.1-hpux10HP-UXB.10.20): 1301728 blocks -1 i-nodes /net/appl/tool-share (svr2:/dsk/4/dsk3/tool/share): 51100992 blocks 4340921 i-nodes /net/appl/netscape (svr2:/dsk/4/dsk3/netscape/pa1.1-hpux10HP-UXB.10.20): 51100992 blocks 4340921 i-nodes /net/appl/gcc-3.3 (svr2:/dsk/4/dsk3/gcc-3.3/pa1.1-hpux10HP-UXB.10.20): 51100992 blocks 4340921 i-nodes /net/appl/gcc-3.2 (svr2:/dsk/4/dsk3/gcc-3.2/pa1.1-hpux10HP-UXB.10.20): 51100992 blocks 4340921 i-nodes /net/appl/tool (svr2:/dsk/4/dsk3/tool/pa1.1-hpux10HP-UXB.10.20): 51100992 blocks 4340921 i-nodes /net/home/wooledg (/home/wooledg ): 658340 blocks 87407 i-nodes /net/home (auto.home ): 0 blocks 0 i-nodes /net/hosts (-hosts ): 0 blocks 0 i-nodes /net/appl (auto.appl ): 0 blocks 0 i-nodes /net/vol (auto.vol ): 0 blocks 0 i-nodes /nfs (-hosts ): 0 blocks 0 i-nodes /home (/dev/vg00/lvol5 ): 658340 blocks 87407 i-nodes /opt (/dev/vg00/lvol6 ): 623196 blocks 83075 i-nodes /tmp (/dev/vg00/lvol4 ): 86636 blocks 11404 i-nodes /usr/local (/dev/vg00/lvol9 ): 328290 blocks 41392 i-nodes /usr (/dev/vg00/lvol7 ): 601750 blocks 80228 i-nodes /var (/dev/vg00/lvol8 ): 110696 blocks 14447 i-nodes /stand (/dev/vg00/lvol1 ): 110554 blocks 13420 i-nodes / (/dev/vg00/lvol3 ): 190990 blocks 25456 i-nodes
بنابراین، اولین سد راه شما، تشخیص آن خواهد بود که ممکن است نسبت به آنکه با کدام سیستم عامل کار میکنید، دستور متفاوتی لازم داشته باشید(به عنوان مثال bdf در HP-UX)، و شاید سیستمهایی باشند که واقعاً در آنها انجام این کار با اسکریپت پوسته به هیچ وجه امکانپذیر نباشد.
برای بقیه این مبحث، ما فرض خواهیم نمود که شما سیستمی با فرمان df همخانواده BSD در اختیار دارید.
مشکل بعدی آن است که قالب خروجی df در تمام سکوها(پلاتفرمها) یکسان نمیباشد.بعضی سکوها از خروجی شش ستونی و برخی از هفت ستونی استفاده میکنند. برخی سکوها(مانند لینوکس)، موقع گزارش فضای واقعی استفاده شده یا در دسترس، به طور پیشفرض بلوکهای یک کیلوبایتی به کار میبرند، دیگران، مانند OpenBSD یا IRIX، به طور پیشفرض بلوکهای 512 بایت را به کار میبرند و برای استفاده از کیلوبایتها گزینه -k را لازم دارند.
بدتر از آن، اغلب یک سطر خروجی در صفحه نمایش به چند سطر تقسیم خواهد شد. برای مثال(لینوکس):
Filesystem 1K-blocks Used Available Use% Mounted on ... svr2:/dsk/4/dsk3/tool/i686Linux2.4.27-4-686 35194552 7856256 25550496 24% /net/appl/tool
اگر نام دستگاه به اندازه کافی طولانی باشد(با فایلسیستمهای متصلشده شبکه خیلی رایج است)، df ممکن است در کوشش برای حفظ خوانایی ستونها برای انسان، خروجی را به دو سطر تقسیم کند. یا شاید نکند...برای مثال، OpenBSD 4.3 را ببینید:
~$ df Filesystem 512-blocks Used Avail Capacity Mounted on /dev/wd0a 253278 166702 73914 69% / /dev/wd0d 8121774 6904178 811508 89% /usr /dev/wd0e 8121774 6077068 1638618 79% /var /dev/wd0f 507230 12 481858 0% /tmp /dev/wd0g 8121774 5653600 2062086 73% /home /dev/wd0h 125253320 116469168 2521486 98% /export ~$ sudo mount 192.168.2.5:/var/cache/apt/archives /mnt ~$ df Filesystem 512-blocks Used Avail Capacity Mounted on /dev/wd0a 253278 166702 73914 69% / /dev/wd0d 8121774 6904178 811508 89% /usr /dev/wd0e 8121774 6077806 1637880 79% /var /dev/wd0f 507230 12 481858 0% /tmp /dev/wd0g 8121774 5653600 2062086 73% /home /dev/wd0h 125253320 116469168 2521486 98% /export 192.168.2.5:/var/cache/apt/archives 1960616 1638464 222560 88% /mnt
اکثر نگارشهای df گزینه -P را در اختیار شما قرار میدهند که به منظور میزان کردن خروجی است... نسبتاً. نگارشهای قدیمیتر OpenBSD حتی موقعی که گزینه -P فراهم میشود، بازهم سطرهای خروجی را تقسیم میکنند، اما لینوکس به طور کلی خروجی برای هر فایلسیستم در یک سطر را تحمیل میکند.
بنابراین، اگر میخواهید اسکریپت نیرومندی بنویسید، نمیتوانید فرض کنید خروجی برای یک فایلسیستم معین در یک سطر منفرد خواهد بود. ما بعداً به این مورد باز میگردیم.
شما مرتب بودن عمودی ستونها را نیز نمیتوانید فرض نمایید:
~$ df -P Filesystem 1024-blocks Used Available Capacity Mounted on /dev/hda1 180639 93143 77859 55% / tmpfs 318572 4 318568 1% /dev/shm /dev/hda5 90297 4131 81349 5% /tmp /dev/hda2 5763648 699476 4771388 13% /usr /dev/hda3 1829190 334184 1397412 20% /var /dev/sdc1 2147341696 349228656 1798113040 17% /data3 /dev/sde1 2147341696 2147312400 29296 100% /data4 /dev/sdf1 1264642176 1264614164 28012 100% /data5 /dev/sdd1 1267823104 1009684668 258138436 80% /hfo /dev/sda1 2147341696 2147311888 29808 100% /data1 /dev/sdg1 1953520032 624438272 1329081760 32% /mnt /dev/sdb1 1267823104 657866300 609956804 52% /data2 imadev:/home/wooledg 3686400 3336736 329184 92% /net/home/wooledg svr2:/dsk/4/dsk3/tool/i686Linux2.4.27-4-686 35194552 7856256 25550496 24% /net/appl/tool svr2:/dsk/4/dsk3/tool/share 35194552 7856256 25550496 24% /net/appl/tool-share
بنابراین، به طور واقعی چه کار میتوانید بکنید؟
گزینه -P را به کار ببرید. حتی اگر همه چیز را 100% سازگار نمیکند، به طور کلی آزاری ندارد. مطابق کُد منبع df.c در coreutils لینوکس، گزینه -P تضمین میکند که خروجی در یک سطر منفرد خواهد بود(اما این فقط برای لینوکس است).
منطقه خود را به C تنظیم کنید. شما نیازی به سرآیند ستونهای غیر انگلیسی درهم پیچیده ندارید.
اگر معتبر است، استفاده از "stat --file-system --format=" را در نظر بگیرید. اگر در موقعیت شما قابلیت حمل مسئلهای نیست، صفحه man فرمان stat را بررسی کنید. در سیستمهای بسیاری قادر خواهید بود اندازه بلوک، تعداد کل بلوکهای دیسک، وتعداد بلوکهای آزاد، همگی در قالب تعیین شده کاربر، را چاپ کنید.
به طور صریح یک فایلسیستم را انتخاب نمایید. اگر نتایج مربوط به یک فایلسیستم را میخواهید از چنین دستوری df -P | grep /dev/hda2 استفاده نکنید. نام یک شاخه یا یک دستگاه را به عنوان یک شناسه به فرمان df بدهید به این طریق فقط خروجی آن فایلسیستم را در مکان اول میبینید.
~$ df -P / Filesystem 1024-blocks Used Available Capacity Mounted on /dev/sda2 8230432 3894360 3917984 50% /
شمارش کلمات خروجی بدون رعایت سطرهایجدید. این یک راه غلبه بر مشکل سطرهایی است که به طور غیرقابل پیشبینی تقسیم میشوند. برای مثال، استفاده از آرایه Bash با نام df_arr:
~$ read -d '' -ra df_arr < <(LC_ALL=C df -P /); echo "${df_arr[11]}" 50%
به طوری که میتوانید ببینید، ما به سادگی تمامی خروجی را به داخل یک آرایه منفرد مکیدیم و سپس کلمه دوازدهم را گرفتیم(شمارش شاخص آرایه از صفر است). نگران آن نبودیم که آیا خروجی تقسیم شده یا خیر، به علت آنکه تعداد کلمات تغییر نمیکند.
حذف علامت درصد، مقایسه عدد با مرز تعیین شده، زمانبندی یک روش خودکار برای اجرای اسکریپت، و غیره. به عنوان تمرینهای شما واگذار گردید.
پرسش و پاسخ 94 (آخرین ویرایش 2013-08-10 20:27:02 توسط nrbg-4dbfc8a9)
اگر ترمینالی دارید که رشتههای escape سازگار با xterm را میشناسد، و شما میخواهید فقط یکبار عنوان آن را تنظیم کنید، میتوانید از چنین تابعی استفاده کنید:
settitle() { printf '\e]2;%s\a' "$*"; }زیرنویس 1
اگر میخواهید نوار عنوان هر بار که فرمانی تایپ میکنید به فرمان جاری در حال اجرا تنظیم گردد، آنوقت راه حل آن تقریباً این است:
trap 'printf "\e]2;%s\a" "$(HISTTIMEFORMAT='' history 1)" > /dev/tty' DEBUG
اگر چه، شماره فرمان تاریخچه را نیز در آنجا مینویسد، و در پوستههای فرعی صریح مانند (cd foo && make) راهاندازی نمیشود.
یا این مورد برای استفاده از فقط نام و شناسههای فرمان ساده جاری:
trap 'printf "\e]2;%s\a" "$BASH_COMMAND" > /dev/tty' DEBUG
برای پوستههای موافق Posix که '\e' را به عنوان رشته کاراکتری که به عنوان Escape تفسیر بشود، تشخیص نمیدهند شاید '\x1b' به جای آن جایگزین بشود.
پرسش و پاسخ 93 (آخرین ویرایش 2011-04-03 15:10:11 توسط adsl-75-61-109-235)
همواره شرایطی ماورای کنترل ما وجود دارد، که ما را به سمت انجام مواردی میراند، که اگر به عهده خودمان بود هرگز انجام آنها را انتخاب نمیکردیم. این مدخل FAQ یکی از آن موقعیتها را تشریح میکند.
یک برنامه CGI میتواند با پارامترهایی که توسط مرورگر شبکه ارسال گردیده، فراخوانی بشود . دو روش(حداقل) برای فراخوانی برنامه CGI وجود دارد: شیوه "GET" و شیوه "POST" . در شیوه "GET" ، پارامترها در یک متغیر محیطی به نام QUERY_STRING برای برنامه CGI فراهم میشوند. پارامترها قالب تعاریف KEY=VALUE را میگیرند(یعنی user=george)، با برخی کاراکترهایی که به هگزادسیمال کدگذاری شدهاند، فاصلهها به عنوان علامت بهاضافه کُد شدهاند، و همه آنها با کاراکترهای
البته اینک ما میدانیم که شما هرگز نمیخواهید یک اسکریپت CGI در Bash بنویسید. بنابراین برای اهداف مورد نظر این مدخل، ما فرض خواهیم نمود که تروریستها همسر و فرزندان شما را دزدیدهاند و اگر شما تقاضای آنان را درنوشتن چنین اسکریپتی اجابت نکنید، آنها را شکنجه میکنند، ضرب و جرح میسازند، و میکشند، «یا وخیمتر».
(موقعیت «یا وخیمتر» شاید به وضوح چیزی مشابه مجبور کردن شما به استفاده از نرمافزارهای مایکروسافت باشد.)
بنابراین، برای یک متغیر معین QUERY_STRING، شاید بخواهیم کلیدها(متغیرها) و مقادیر آنها را استخراج کنیم، به طوری که بتوانیم آنها را در اسکریپت به کار ببریم.
روش سریع، آسان، و خطرناک برای پردازش QUERY_STRING، تبدیل &ها به ;ها و سپس استفاده از فرمان eval برای انجام تخصیص آنها میباشد. به هر حال استفاده از eval به شدت دلسرد کننده است. این است که ما همیشه میگوییم، اگر راه دیگری برای انجام آن وجود دارد، از eval پرهیز نمایید.
# cgi در خواندن رشته ورودی در if [ "$QUERY_STRING" ]; then foo=$QUERY_STRING else read foo fi # (میماند برای تمرین خواننده) "&" تبدیل مقداری رشته کُد شده و مواردی مانند # روی رشته eval اجرای eval $foo # را در یک فیلد فرم وب قرار "/bin/rm -rf /" کنار بنشینید و تماشاکنید، کاربر # .نباشد میتواند به بخشی از سیستمفایل صدمه وارد کند root داده است، که حتی اگر # .یک رشته خطرناک دیگر می تواند بمب خوشهای باشد
به جای گفتن آنکه پوسته هر کُدِ فراهم شده توسط کاربر در پارامترها را اجرا کند، رویکرد بهتر استخراج هر زوج متغیر/مقدار، و تخصیص آنها در متغیرهای پوسته، به طور یک به یک، بدون اجرای آنها میباشد. این کار یک تخصیص متغیر غیر مستقیم نیاز دارد، که به معنی استفاده از بعضی ترفندکاریهای مختص پوسته میباشد. ما این ترفند را با ترکیب دستوری Bash مینویسیم، تبدیل آن به پوسته ksh یا Bourne به عنوان تمرین واگذار میشود.
# Bash # cgi خواندن رشته ورودی در if [ "$QUERY_STRING" ]; then foo=$QUERY_STRING else read -r foo fi # میباشد name=Fred+Flintstone&city=Bedrock شامل موردی مشابه foo # متصل شدهاند رفتار میکند & که با key=value این مانند یک لیست از عبارتهای # تکرار روی عناصر لیست و انجام عمل تخصیص هریک IFS='&'; set -f for i in $foo; do declare "$i" done unset IFS # یک متغیر پوسته با همان نام خواهد شد CGI اکنون هر پارامتر # .بهتر است شما بدانید نامها کدام هستند، چون آنها را پیگردی نمیکنیم # است. فاصله به عنوان + کُد شده است "urlencoded" هر متغیر بازهم # هگزادسیمال است xx کُد شدهاند که %xx اقلام مختلف به عنوان # را به کار ببریم "name" فرض کنید میخواهیم پارامتری به نام # .اول رمزگشایی فاصلهها name=${name//+/ } # ما ترفند دیگری برای انجام آن به کار میبریم %xx اکنون رمز گشایی کاراکترهای # تعویض میکنیم \x را با % اول تمام علائم # ها میشود\xxx را به کار میبریم برای آنکه موجب ارزیابی تمام echo -e دوم، دستور name=${name//\%/\\x} name=$(echo -e "$name") # این کار را قبل از تکرار و تخصیص حلقه انجام ندادیم چون اگر این کار را میکردیم # کُد شده (یا هر کاراکتر بدخواهانه) است & آنوقت یک پارامتر که شامل یک کاراکتر # .موجب اندوه بسیار خواهد شد. ما باید این کار را در اینجا انجام بدهیم # انجام بدهید "name" حالا هر کاری مایل هستید با
در حالیکه شاید این روش قدری کمتر واضح باشد، از مشکل امنیتی بزرگی اجتناب میکند که eval دارای آن است: اجرای هر فرمان دلخواهی که ممکن بود کاربر، علاقمند به ورود آن از طریق یک فُرم وِب باشد. به طور واضح این یک بهبود است.
در این نگارش هنوز کاستیهایی وجود دارد. برای مثال، ما برای تضمین معتبر یا ایمن بودن نام متغیر پوسته، هیچگونه اعتبارسنجی روی طرف چپ(نام متغیر) در هر زوج key=value انجام نمیدهیم. اگر کاربر یک PATH= را در یک پارامتر استعلام وارد کند چه؟
حتی یک رویکرد بهتر، میتواند قرار دادن زوجهای کلید\متغیر داخل یک آرایه انجمنی باشد. آرایههای انجمنی در ksh93 و bash 4.0 معتبر هستند، اما در POSIX یا پوستههای Bourne خیر. آنهابرای نگهداری زوجهای key/value طراحی شدهاند که در آن کلیدها میتوانند رشتههای اختیاری باشند، بنابراین به نظر میرسد برای این کار مناسب هستند.
# Bash 4+ # cgi خواندن رشته ورودی if [ "$QUERY_STRING" ]; then foo=$QUERY_STRING else read -r foo fi # .برقراری آرایه انجمنی برای نگهداری پارامترهای پرس وجو declare -A q # key=value+%41%42%43 تکرار روی عناصر # .جدا سازی کلید و کمیت، و انجام رمزگشایی کمیت IFS='&'; set -f for i in $foo; do IFS='=' read key value <<< "$i" # هاbackslash حذف -- اول پاک سازی : مراحل رمزگشایی # .دوم، علامتهای بعلاوه تبدیل به فاصله میشوند # .میشوند \x سوم، علائم درصدتبدیل به # .را موجب گردد printf چیزی باقی نمیگذارد که بتواند به طور غیر منتظره یک بسط # .ها مال ما هستند و هیچ علامت درصد باقی نماندهbackslashe تمام value=${value//\\/} value=${value//+/ } value=${value//\%/\\x} printf -v final -- "$value" q["$key"]="$final" done unset IFS # .به کار ببریم q اکنون میتوانیم پارامترها را از آرایه انجمنی با نام # ${!q[*]} اگر لیستی از کلیدها را لازم داشته باشیم، این است
در اینجا مرحله پاکسازی بینهایت مهم است. بدون آن اقدام احتیاطی، فرمان printf میتواند به واسطه یک رشته قالببندی مهاجم، آسیب پذیر باشد. گزینه printf -v varname در هر نگارش از bash که از آرایههای انجمنی پشتبانی کند، معتبر است، بنابراین میتوانیم از آن در اینجا استفاده کنیم. خیلی بیشتر از فراخوانی پوسته فرعی مؤثر است. همچنین از مشکلات بالقوه echo -e در صورتی که اتفاقاً value موردی مانند -n باشد، اجتناب نمودهایم.
به طور تکنیکی، خصوصیات CGI چندین نمونه از یک کلید را در یک استعلام منفرد اجازه میدهد. برای مثال، group=managers&member=Alice&member=Charlie یک رشته کاملاً مشروع پرس وجو میباشد. هیچ یک از رویکردها در این صفحه این حالت را مدیریت نمیکند(حداقل نه به طریقی که ما احتمالاً آنرا روش صحیح در نظر بگیریم). خوشبختانه، غالباً اینطور نیست که شما بخواهید یک اسکریپت CGI مانند این بنویسید، و در هر حال، شما مجبور نیستید برای انجام این وظیفه از bash استفاده کنید.
پرسش و پاسخ 92 (آخرین ویرایش 2010-04-16 23:58:26 توسط GreyCat)
COLUMNS و LINES در وضعیت محاورهای توسط BASH تنظیم میشوند، آنها به طور پیشفرض در یک اسکریپت در دسترس نیستند. در اکثر سیستمها خودتان میتوانید با ترمینال پرس و جو کنید:
unsup() { echo "Your system doesn't support retrieving $1 with tput. Giving up." >&2; exit 1; } COLUMNS=$(tput cols) || unsup cols LINES=$(tput lines) || unsup lines
Bash به طور خودکار متغیرهای COLUMNS و LINES را موقعی که پوسته محاورهای تغییر اندازه داده میشود، به هنگام میکند. اگر شما این متغیرها را در اسکریپت تنظیم میکنید و میخواهید موقعی که اندازه ترمینال تغییر میکند، آنها به هنگام شوند، یعنی به مجرد دریافت سیگنال SIGWINCH، میتوانید خودتان یک trap تنظیم کنید:
trap 'COLUMNS=$(tput cols) LINES=$(tput lines)' WINCH
همچنین میتوانید در سرآیند اسکریپت، پوسته را به عنوان محاورهای تنظیم کنید:
#!/bin/bash -i echo $COLUMNS
به هر حال، این مورد دارای اشکالاتی هست:
بررسی بواسطه گزینه -i به منظور تعیین آنکه آیا پوسته محاورهای است، و سپس انصراف یا درست رفتار نکردن، برای اسکریپتها خیلی غیر معمول نیست، اگرچه بهترین کار نمیباشد. هیچ روش کاملاً خالی از نقصی برای بررسی این مطلب، وجود ندارد، بنابراین بعضی از اسکریپتها ممکن است به سبب آن ناموفق شوند.
اجرا با گزینه -i فایل .bashrc را منبع میکند، و گزینههای مختلفی از قبیل job-control را تنظیم میکند که شاید آثار جانبی ناخواسته داشته باشند.
اگر چه از لحاظ تکنیکی میتوانید -i را در میان اسکریپت تنظیم کنید، اما تأثیری بر تنظیم COLUMNS و LINES ندارد -- -i باید ابتدا، موقعی که Bash احضار میشود برقرار باشد.
معمولاً Bash موقعی که ترمینال شما سیگنال SIGWINCH نشان دهنده تغییر اندازه را ارسال میکند،COLUMNS و LINES را به روز رسانی میکند. برخی ترمینالها ممکن است این کار را انجام ندهند، بنابراین اگر متغیرهای شما حتی موقعی که یک پوسته محاورهای در حال اجرا است به روز رسانی نمیشوند، استفاده از shopt -s checkwinsize را امتحان کنید. این دستور باعث استعلام ترمینال توسط Bash پس از هر فرمان میشود، بنابراین تنها در صورتی که واقعاً لازم است از آن استفاده کنید.
البته، tput به ترمینال نیاز دارد. مطابق POSIX، اگر خروجی استاندارد tty نباشد، نتایج تعیین شده نیستند، و stdin استفاده نمیشود، اگر چه بعضی پیادهسازیها ممکن است سعی کنند به هرحال از آن استفاده کنند. در لینوکس OpenBSD و Gentoo (و ظاهراً حداقل برخی لینوکسهای دیگر)، دست کم یکی از stdout یا stderr باید tty باشد، وگرنه tput فقط بعضی مقادیر پیشفرض را برمیگرداند.
linux$ tput -S <<<$'cols\nlines' 2>&1 | cat 80 24 openbsd$ tput cols lines 2>&1 | cat 80 24
پرسش و پاسخ 91 (آخرین ویرایش 2013-01-11 22:11:44 توسط GreyCat)