آموزش‌های خط فرمانی

این وبلاگ تلاش می‌کند گامی در حد بضاعت در جهت آموزش خط فرمان و اسکریپت‌نویسی پوسته گنو-لینوکس بردارد.

آموزش‌های خط فرمانی

این وبلاگ تلاش می‌کند گامی در حد بضاعت در جهت آموزش خط فرمان و اسکریپت‌نویسی پوسته گنو-لینوکس بردارد.

افزودن پشتیبانی محلی به اسکریپت


چگونه پشتیبانی محلی را به اسکریپت‌های bash خود اضافه کنید

جستجو برای مثالهای چگونگی افزودن محلی‌سازی ساده به اسکریپت‌های bash خودتان، و چگونگی انجام آزمایش؟ احتمالاً آنچه شما می‌خواهید این است....

نخست، چند متغیر که باید آنها را درک کنید

قبل از اینکه حتی بتوانیم شروع کنیم، باید تمام متغیرهای محیط منطقه را شیرفهم بشویم. این مورد پایه‌ای است و در محل‌هایی که مردم به مستندات(صفحات man، و غیره) توجه می‌نمایند، برای مستندسازی بینهایت مهم است. بعضی از این متغیرها ممکن است در سیستم شما فراهم نباشند، زیرا به نظر می‌رسد استانداردهای گوناگونی برای توسعه و رقابت وجود دارد....

در سیستم‌های اخیر گنو، متغیرها به این ترتیب استفاده می‌شوند:

  1. اگر LANGUAGE تنظیم است، آن را استفاده کنید، مگر اینکه LANG برابر با C تنظیم شود، در چنین حالتی LANGUAGE صرف نظر می‌شود. همچنین، بعضی برنامه‌ها در حقیقت ابداً از LANGUAGE استفاده نمی‌کنند.

  2. در غیر این صورت، اگر LC_ALL تنظیم باشد، آنرا به کار ببرید.
  3. و گرنه، در صورتی که متغیر ‎LC_*‎ که چنین حالتی را پوشش می‌دهد برقرار باشد، آن را استفاده نمایید. (برای مثال، LC_MESSAGES پیغام‌های خطا را پوشش می‌دهد.)
  4. در غیر این صورت، LANG را به کار ببرید.

این به آن معنا می‌باشد که شما باید اول محیط جاری را برای دیدن اینکه کدام یک از این متغیرها قبلاً تنظیم شده‌اند، کنترل نمایید. اگر آنها تنظیم شده باشند و شما ندانید، ممکن است آنها در بررسی‌های شما دخالت کرده، شما را گیج کنند.

$ env | egrep 'LC|LANG'
LANG=en_US.UTF-8
LANGUAGE=en_US:en_GB:en

این مثالی از یک سیستم دبیان است. در این حالت، متغیر LANGUAGE برقرار است، یعنی ما هر آزمایشی انجام بدهیم که با تغییر LANG درگیر بشود احتمالاً ناموفق است، مگر اینکه ما LANGUAGE را هم تغییر بدهیم. حال در اینجا مثال دیگری از یک سیستم دبیان دیگر:

$ env | egrep 'LC|LANG'
LANG=en_US.utf8

در این حالت، تغییر LANG واقعاً کار خواهد کرد. سندی که یک کاربر در این سیستم، در مورد چگونگی انجام آزمایش محلی‌سازی بنویسد، شاید دستورالعمل‌هایی ایجاد کند که برای کار کردن کاربر در سیستم اول شکست بخورد....

بنابراین، پیش بروید و با سیستم خودتان بررسی کنید و ببینید کدام کار می‌کند و کدام کار نمی‌کند. ممکن است شما اصلاً متغیر LANGUAGE نداشته باشید(محصوصاً اگر در گنو-لینوکس نباشید)، از اینرو ممکن است تنظیم آن برای شما نتیجه‌ای نداشته باشد. شاید لازم باشد دستور ‎locale -a‎ را به کار ببرید تا ببینید کدام تنظیمات منطقه معتبر هستند. ممکن است لازم باشد یک مجموعه کاراکتری را در متغیر LANG تعیین نمایید(به عنوان مثال ‎es_ES.utf8‎ به جای ‎es_ES‎). شاید برای اینکه موجب کار کردن آنها بشوید، بایستی مناطق را در سیستم عامل خود ایجاد کنید(پردازشی که فراتر از حوزه این صفحه است، اما در سیستم دبیان متشکل از اجرای ‎dpkg-reconfigure locales‎ و پاسخ دادن به پرسش‌ها است).

تلاش کنید به آنجا برسید که حداقل در دو زبان بتوانید پیغام‌های خطا تولید کنید :

$ wc -q
wc: invalid option -- 'q'
Try `wc --help' for more information.
$ LANGUAGE=es_ES wc -q
wc: opción inválida -- q
Pruebe `wc --help' para más información.

وقتی که بتوانید آن کار را به طور قابل قبولی انجام بدهید، می‌توانید کار واقعیِ تولید اسکریپت bash با محلی‌سازی را شروع کنید.

نشانه‌گذاری رشته‌های قابل ترجمه

این قسمت حداقل از جهت فهمیدن، ساده‌ترین بخش است. هر رشته در ‎$"..."‎ با استفاده از امکانات پشتیبانی زبان محلی (NLS) سیستم ترجمه می‌شود. تمام رشته‌های ثابت در برنامه خود را که می‌خواهید ترجمه شوند، یافته و علامت‌گذاری کنید. رشته‌های شامل متغیرها یا سایر جایگزینی‌ها را علامت نزنید. برای مثال

#!/bin/bash
echo $"Hello, world"

(به طوری که می‌توانید ببینید، ما در اینجا با مورد بسیار ساده شروع می‌کنیم.)

Bash (حداقل بعد از ‎4.0‎) بسط منطقه را قبل از سایر جایگزینی‌ها انجام می‌دهد. بدین معنی، در حالتی مانند این مورد:

echo $"The answer is $answer"

رشته لفظی ‎$answer‎ بخشی از رشته علامت زده خواهد شد. ترجمه نیز باید شامل ‎$answer‎ باشد، و bash جایگزینی متغیر را در رشته ترجمه شده انجام خواهد داد. ترتیبی که bash این جایگزینی‌ها را انجام می‌دهد یک حفره امنیتی بالقوه را مطرح می‌کند که ما در اینجا آن را پوشش نخواهیم داد. (وصله‌ای ارائه شده است، اما هنوز خیلی ابتدایی است....)

تولید و(یا) ادغام فایلهای PO

سپس، آنچه ‎"PO files"‎ نامیده شده را بواسطه برنامه خودتان تولید کنید. این فایلها محتوی رشته‌هایی که علامت‌گذاری کرده‌ایم، و ترجمه آنها(که بعداً قرار خواهیم داد) هستند.

با ایجاد یک فایل ‎*.pot‎ که یک الگو می‌باشد، شروع می‌کنیم.

bash --dump-po-strings hello > hello.pot

خروجی که این کد تولید می‌کند به این شکل خواهد بود:

#: hello:5
msgid "Hello, world"
msgstr ""

نام فایل شما(بدون پسوند ‎.pot‎) ‏domain‏ متن قابل ترجمه شما نامیده می‌شود. domain در این زمینه مشابه نام یک بسته( package) است. به عنوان مثال، بسته coreutils گنو شامل تعداد بسیاری برنامه‌های کوچک است، اما تمام آنها متفقاً توزیع می‌گردند، و بنابراین، معقول است تمام ترجمه‌های آنها نیز با یکدیگر باشند. ما در این مثال از domain با نام hello استفاده می‌کنیم. در یک مثال بزرگترِ محتوی تعداد زیادی برنامه در یک مجموعه، احتمالاً باید نام کل مجموعه را استفاده کنیم.

این الگو برای هر زبانی که می‌خواهیم پشتیبانی نماییم، یکبار کپی خواهد شد. بیایید فرض کنیم می‌خواهیم ترجمه‌های اسپانیایی و فرانسوی را برای برنامه‌های خود پشتیبانی نماییم. ما دو فایل PO (یکی برای هر ترجمه) ایجاد خواهیم نمود، پس اجازه بدهید دو شاخه فرعی بسازیم و الگو را در هر یک از آنها کپی کنیم:

mkdir es fr
cp hello.pot es/hello.po
cp hello.pot fr/hello.po

این است آنچه ما در نوبت اول انجام می‌دهیم. اگر از قبل بعضی فایلهای جزئی یا حتی ترجمه تکمیل شده PO درآنجا وجود داشت، ما نمی‌خواستیم آنها را رونویسی کنیم. پس به جای آن، موارد ترجمه جدید را در فایل قدیمی PO ادغام می‌نمودیم. برای این کار از ابزار ویژه‌ای به نام msgmerge استفاده می‌نماییم. اجازه بدهید فرض کنیم که مقدار بیشتری کُد(و رشته‌های قابل ترجمه) به برنامه خودمان اضافه می‌کنیم:

vi hello
bash --dump-po-strings hello > hello.pot
msgmerge --update es/hello.po hello.pot
msgmerge --update fr/hello.po hello.pot

مؤلف اصلی این صفحه مقداری یادداشت تهیه نموده است که من در اینجا دست‌نخورده رها می‌کنم. شاید سودمند واقع بشوند...؟

# step 5: try to merge existing po with new updates
# remove duplicated strings by hand or with sed or something else
# awk '/^msgid/&&!seen[$0]++;!/^msgid/' lang/nl.pot > lang/nl.pot.new
msgmerge lang/nl.po lang/nl.pot

# step 5.1: try to merge existing po with new updates
cp --verbose lang/pct-scanner-script-nl.po lang/pct-scanner-script-nl.po.old
awk '/^msgid/&&!seen[$0]++;!/^msgid/' lang/pct-scanner-script-nl.pot > lang/pct-scanner-script-nl.pot.new
msgmerge lang/pct-scanner-script-nl.po.old lang/pct-scanner-script-nl.pot.new > lang/pct-scanner-script-nl.po

# step 5.2: try to merge existing po with new updates
touch lang/pct-scanner-script-process-nl.po lang/pct-scanner-script-process-nl.po.old
awk '/^msgid/&&!seen[$0]++;!/^msgid/' lang/pct-scanner-script-process-nl.pot > lang/pct-scanner-script-process-nl.pot.new
msgmerge lang/pct-scanner-script-process-nl.po.old lang/pct-scanner-script-process-nl.pot.new > lang/pct-scanner-script-process-nl.po

ترجمه رشته‌ها

این مرحله‌ای است که ‎100%‎ آن کار انسانی است. فایل PO هر زبان را ویرایش کنید و محل‌های خالی را پُر کنید.

#: hello:5
msgid "Hello, world"
msgstr "Hola el mundo"

#: hello:6
msgid "How are you?"
msgstr ""

نصب فایلهای MO

سیستم عامل شما، اگر شما را تا اینجا پیش‌آورده، احتمالاً از قبل چند برنامه محلی شده با کاتالوگ‌های ترجمه نصب شده درجایی مانند ‎/usr/share/locale‎ (یا جای دیگری) دارد. اگر می‌خواهید ترجمه‌های شما نیز در همان محل نصب بشوند، باید از اختیارات کاربر ارشد برخوردار باشید، و شما باید domain یا (namespace) ترجمه خود را به طوری مدیریت نمایید که از برخورد با تمام بسته‌های سیستم‌عامل اجتناب گردد.

اگر می‌خواهید از محل استاندارد سیستم برای ترجمه‌هایتان استفاده کنید، آنوقت فقط لازم است در اندیشه انجام یک تغییر در برنامه خود باشید: تنظیم متغیر TEXTDOMAIN.

#!/bin/bash
TEXTDOMAIN=hello

echo $"Hello, world"
echo $"How are you?"

این متغیر به bash و کتابخانه‌های سیستم می‌گوید از کدام فایل MO در محل استاندارد استفاده نمایند. اگر می‌خواهید از مکان غیر استاندارد استفاده نمایید، آنوقت باید آن محل را نیز در متغیری به نام TEXTDOMAINDIR قرار بدهیم:

#!/bin/bash
TEXTDOMAINDIR=/usr/local/share/locale
TEXTDOMAIN=hello

echo $"Hello, world"
echo $"How are you?"

بنابر نیاز خود یکی از این موارد را به کار ببرید.

یک فایل MO در اصل یک فایل کامپایل شده PO می‌باشد. برنامه‌ای به نام msgfmt عهده‌دار این کامپایل نمودن است. اکنون ما فقط باید بگوییم فایل PO کجاست و فایل MO کجا نوشته شود.

msgfmt -o /usr/share/locale/es/LC_MESSAGES/hello.mo es/hello.po
msgfmt -o /usr/share/locale/fr/LC_MESSAGES/hello.mo fr/hello.po

یا

mkdir -p /usr/local/share/locale/{es,fr}/LC_MESSAGES
msgfmt -o /usr/local/share/locale/es/LC_MESSAGES/hello.mo es/hello.po
msgfmt -o /usr/local/share/locale/fr/LC_MESSAGES/hello.mo fr/hello.po

(اگر ما بیش از دو ترجمه برای پشتیبانی داشتیم، می‌توانستیم تقلید از ساختار ‎/usr/share/locale‎ برای تسهیل کپی گروهی فایلهای MO از دایرکتوری محلی به مخزن سیسم‌عامل را انتخاب نماییم. این به عنوان تمرین واگذار شد.)

آزمون!

آنچه قبلاً در باره متغیرهای محیط منطقه گفتیم را به خاطر بیاورید...مثالهای اینجا ممکن است در سیستم شما کار بکنند یا نکنند.

برنامه gettext می‌تواند برای بازیابی ترجمه‌های شخصی از کاتالوگ به کار برود:

$ LANGUAGE=es_ES gettext -d hello -s "Hello, world"
Hola el mundo

همه رشته‌های ترجمه نشده به حال خود رها می‌شوند:

$ LANGUAGE=es_ES gettext -d hello -s "How are you?"
How are you?

و سرانجام، هیچ جایگزینی برای اجرای واقعی خود برنامه وجود ندارد:

wooledg@wooledg:~$ LANGUAGE=es_ES ./hello
Hola el mundo
How are you?

به طوری که می‌توانید ببینید، هنوز برای مثال ما مقدار دیگری ترجمه وجود دارد که باید انجام بشود. برگردید به کار....

منابع


CategoryShell

پرسش و پاسخ 98 (آخرین ویرایش ‎2012-03-11 00:08:39‎ توسط host-216-57-70-194)


تعیین لینک‌های خراب


مستندات در این مورد مبهم است، اما می‌توانید به نوعی با دستورات داخلی پوسته آن را انجام دهید:

# Bash
if [[ ( -L $name ) && ( ! -e $name ) ]]
then echo "$name is a dangling symlink"
fi

صفحه man در Bash به شما می‌گوید که ‎"-L"‎ «اگر فایل موجود باشد و پیوند نمادین باشد» True را برمی‌گرداند، و ‎"-e"‎ «اگر فایل موجود باشد» True را باز می‌گرداند. آنچه می‌تواند روشن نباشد، آنست که ‎"-L"‎ ملاحظه می‌کند که فایل خودش لینک باشد. اما، برای ‎"-e"‎، فایل مقصد لینک نمادین است(جایی که لینک به آن اشاره می‌کند). به این دلیل است که شما برای دیدن آنکه پیوند نمادین منفصل است، به هر دو بررسی نیاز دارید، ‎"-L"‎ خود لینک را بررسی می‌کند، و ‎"-e"‎ جایی که لینک به آن اشاره می‌کند را بررسی می‌کند.

POSIX به طور مشابهی دارای همین بررسی‌ها می‌باشد، بنابراین اگر بنا به دلایلی شما نمی‌توانید از فرمان(ارجح) ‎[[ استفاده کنید، همان بررسی می‌تواند با استفاده ازفرمان قدیمی‌تر ‎[‎ انجام بشود:

# POSIX
if [ -L "$name" ] && [ ! -e "$name" ]
then echo "$name is a dangling symlink"
fi


CategoryShell

پرسش و پاسخ 97 (آخرین ویرایش ‎2012-01-13 09:52:29‎ توسط ght)


پرسش و پاسخ شماره ۹۶


ssh کرانه‌های کلمات مرا می‌خورد! نمی‌توانم ‎ ssh remotehost make CFLAGS="-g -O"‎ را انجام بدهم!

ssh رفتار فرمان راه دور پوسته یونیکس (rsh یا remsh) شامل این باگ، را شبیه‌سازی می‌کند. چند روش برای عبور موقت موجود است، و به طور دقیق وابسته آنست که شما چه چیزی لازم دارید.

نخست، اینجا توضیح کاملی از مشکل است:

~$ ~/bin/args make CFLAGS="-g -O"
2 args: 'make' 'CFLAGS=-g -O'
~$ ssh localhost ~/bin/args make CFLAGS="-g -O"
Password: 
3 args: 'make' 'CFLAGS=-g' '-O'

آنچه رخ می‌دهد آنست که در طرف سرویس‌گیرنده، فرمان و شناسه‌هایش در یک رشته با یکدیگر منگنه می‌شوند، سپس از طریق ارتباط ssh به طرف سرویس‌دهنده رانده می‌شود، جایی که آن رشته به عنوان یک شناسه، برای تفکیک مجدد در اختیار پوسته شما قرار داده می‌شود. این آنچه ما می‌خواهیم نیست.

ساده‌ترین راه عبور از این مشکل چسباندن همه چیز باهم در یک نقل‌قول منفرد، و اضافه کردن دستی نقل‌قول‌ها عیناً در محل‌های صحیح، می‌باشد، تا آماده کار با آن بشویم.

~$ ssh localhost '~/bin/args make CFLAGS="-g -O"'
Password: 
2 args: 'make' 'CFLAGS=-g -O'

پوستهِ رویِ میزبان راه دور شناسه را مجدداً تجزیه می‌کند، آن را به کلمات می‌شکند، و سپس اجرا می‌نماید.

مشکل نخست با این رویکرد آنست که، خسته کننده است . اگر ما از قبل هردو نوع نقل‌قول ، و جایگزینی‌های بسیار زیاد پوسته را که باید انجام بشوند، داشته باشیم، آنوقت شاید عاقبت مجبور شویم مقدار انبوهی را با اضافه نمودن \ جهت حفظ صحت امور بازنویسی نماییم، و به همین منوال. دومین مشکل این روش آنست که، اگر فرمان کامل ما پیشاپیش معلوم نباشد خیلی خوب کار نمی‌کند -- به عنوان مثال، اگر ما یک WrapperScript بنویسیم.

روش دیگر حل و فصل این مسئله آن است که به جای عبور دادن فرمان(ها) به عنوان شناسه، آنها را به عنوان stdin برای پوسته راه دور ارسال کنیم. این روش در همه حالت‌ها کار نخواهد کرد، این بدان خاطر است که فرمان در حال اجرا روی سیستم راه دور نمی‌تواند از stdin برای مقصود دیگری استفاده نماید، چون ما در حال مسدود نمودن stdin برای ارسال فرمانهای خود می‌باشیم. اما در وضعیت‌هایی که می‌تواند استفاده شود، کاملاً خوب کار می‌کند:

# POSIX
# .برای استفاده توسط برنامه راه دور در دسترس نخواهد بود‎
ssh remotehost sh <<EOF
make CFLAGS="-g -O"
EOF

حال اجازه بدهید مشکل واقع‌گرایانه‌تری را در نظر بگیریم: می‌خواهیم یک اسکریپت wrapper بنویسیم که make را روی میزبان راه دور با شناسه‌های فراهم شده توسط کاربر که دست نخورده همراه با آن عبور داده می‌شوند، فراخوانی می‌کند. این بسیار دشوارتر از آنست که در نگاه اول به نظر می‌رسد، به دلیل آنکه ما نمی‌توانیم همه چیز را در یک کلمه به یکدیگر بچسبانیم -- فراخواننده اسکریپت می‌تواند شناسه‌های واقعاً پیچیده، نقل‌قول‌ها، و نام مسیرهای شامل فاصله‌ها، و فوق‌کاراکترهای پوسته را استفاده کند، که لازم است تمام اینها به دقت حفاظت بشوند. خوشبختانه، bash روشی جهت محافظت از چنین مواردی به طور سالم برای ما فراهم نموده است: ‎printf %q‎. با یک آرایه و یک حلقه همراه یکدیگر، می‌توانیم یک wrapper بنویسم:

# Bash < 3.1
# sh باشد نه BASH پوسته حساب کاربری شما روی میزبان راه دور باید
unset a i
for arg; do
  a[i++]=$(printf %q "$arg")
done
exec ssh remotehost make "${a[@]}"

# Bash 3.1 and up
# sh باشد نه BASH پوسته حساب کاربری شما روی میزبان راه دور باید
unset a
for arg; do
  printf -v temp %q "$arg"
  a+=("$temp")
done
exec ssh remotehost make "${a[@]}"

# Bash 4.1 and up
# sh باشد نه BASH پوسته حساب کاربری شما روی میزبان راه دور باید
unset a i
for arg; do
  printf -v 'a[i++]' %q "$arg"
done
exec ssh remotehost make "${a[@]}"

اگر نیاز داشته باشیم که در میزبان راه دور قبل از اجرای make دایرکتوری را نیز تغییر بدهیم، می‌توانیم آن را هم اضافه کنیم:

# Bash < 3.1
# sh باشد نه BASH پوسته حساب کاربری شما روی میزبان راه دور باید
unset a i
for arg; do
  a[i++]=$(printf %q "$arg")
done
exec ssh remotehost cd "$PWD" "&&" make "${a[@]}"

(اگر ‎$PWD‎ شامل کاراکتر فاصله باشد، آنوقت لازم است آن نیز با همان ترفند ‎printf %q‎ محافظت گردد، که به عنوان تمرین به خواننده واگذار می‌گردد.)


CategoryShell

پرسش و پاسخ 96 (آخرین ویرایش ‎2012-06-18 13:16:12‎ توسط geirha)


پرسش و پاسخ شماره ۹۵


میخواهم لیست خیلی بلند شناسه‌ها را دریافت کنم. چطور می‌توانم یک لیست بزرگ قابل توجه را پردازش نمایم؟

ابتدا، بیایید برخی موضوعات زمینه‌ای را بازبینی کنیم. موقعی که یک پردازش می‌خواهد پردازش دیگری را اجرا کند، یک فرزند ‎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 و اندازه بزرگترین عنصر محاسبه کنید، اما بازهم باید «ضرایب جبرانی» برای اندازه محیط و غیره را در نظر بگیرید. آسان‌تر است فقط یک مقدار محافظه‌کارانه را به امید آنکه بهترین مقدار باشد انتخاب نمود.


CategoryShell

پرسش و پاسخ 95 (آخرین ویرایش ‎2010-01-06 14:23:30‎ توسط GreyCat)


اخطار در مورد کمبود فضای دیسک


می‌خواهم وقتی دیسک پُر می‌شود یک هشدار دریافت کنم(با تجزیه خروجی df).

متأسفانه، تجزیه خروجی فرمان 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%

    به طوری که می‌توانید ببینید، ما به سادگی تمامی خروجی را به داخل یک آرایه منفرد مکیدیم و سپس کلمه دوازدهم را گرفتیم(شمارش شاخص آرایه از صفر است). نگران آن نبودیم که آیا خروجی تقسیم شده یا خیر، به علت آنکه تعداد کلمات تغییر نمی‌کند.

حذف علامت درصد، مقایسه عدد با مرز تعیین شده، زمان‌بندی یک روش خودکار برای اجرای اسکریپت، و غیره. به عنوان تمرینهای شما واگذار گردید.


CategoryShell

پرسش و پاسخ 94 (آخرین ویرایش ‎2013-08-10 20:27:02‎ توسط nrbg-4dbfc8a9)