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

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

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

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

توابع سودمند warn و die


توابع سودمند همگانی(warn و die)

(اگر شما در جستجوی گزینه پردازش بوده‌اید، پرسش و پاسخ شماره ۳۵ را ملاحظه کنید.) در مورد توابع ذیل بارها در ‎#bash‎ سؤال شده است، بنابراین امیدواریم آنها برایتان مفید باشد.

##
# warn: چاپ می‌کند stderr پیغامی در‎
# warn "format" ["arguments"...] :طرز استفاده ‎
#
warn() {
  local fmt="$1"
  shift
  printf "script_name: $fmt\n" "$@" >&2
}

###
### زیرین "die" سه تابع‎
### فوق "warn" بر اساس تابع‎
###

##
# چاپ می‌کند stderr یک پیغام در ‎:‎ (نگارش ساده) die ‎
# .و با وضعیت خروج آخرین فرمان خارج می‌شود‎
# 
# ‎some_command || die "پیغام شما" ‎["arguments"...]‎   :‎طرز استفاده‎
#
die () {
  local st="$?"
  warn "$@"
  exit "$st"
}

##
#  stderr یک پیغام در‏ ‎:‎ (نگارش وضعیت صریح) die‎
#  .چاپ می‌کند و با وضعیت معین شده خارج می‌شود‎
# if فلان; then die status_code "پیغام"  ‎["arguments"...]‎; fi : طرز استفاده ‎
#
die() {
  local st="$1"
  shift
  warn "$@"
  exit "$st"
}

##
#  stderr پیغامی در‏‎:‎ (نگارش وضعیت اختیاری) die ‎
#  چاپ می‌کند و با وضعیت خروج تعیین شده یا‎
# .وضعیت خروج آخرین فرمان خارج می‌شود‎
#  some_command || die [status code] "message" ["arguments"...] :طرز استفاده ‎
#
die() {
  local st="$?"
  if [[ "$1" != *[^0-9]* ]]; then
    st="$1"
    shift
  fi
  warn "$@"
  exit "$st"
}


CategoryShell

پرسش و پاسخ 101 (آخرین ویرایش ‎2013-08-27 00:04:03‎ توسط 162)


دستکاری رشته‌ها در Bash


در bash چگونه می‌توانم رشته‌ها را دستکاری کنم؟

Bash می‌تواند با رشته‌ها عملیات انجام بدهد. عملیات رشته‌ای فراوان. این یک مقدمه برای دستکاری‌های رشته در bash و شگردهای مربوطه می‌باشد. با پرسش بسط پارامتر همپوشانی دارد، اما اطلاعاتی که در اینجا ارائه شده است، یک حالت مبتدی پسندتری دارد(امیدواریم).

ترکیب دستوری بسط پارامتر

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

fruit=apple

و می‌توانیم آن مقدار را دوباره با استفاده از بسط پارامتر بخوانیم:

$fruit

توجه نمایید، در هرحال، آن ‎$fruit‎ یک بسط می‌باشد -- یک نام، نه یک فعل -- و بنابراین به طور طبیعی ما نیاز داریم که آن را در نوعی دستور قرار بدهیم. همچنین، نتایج یک بسط پارامتر غیرنقل‌قولی به چندین کلمه تفکیک و به نام فایلها بسط داده خواهد شد، که معمولاً ما نمی‌خواهیم. بنابراین، همیشه باید بسط‌های پارامتر خودمان را نقل‌قولی کنیم مگر اینکه با یک وضعیت ویژه‌ای سروکار داشته باشیم.

از این جهت، برای دیدن مقدار پارامتر(از قبیل یک متغیر):

echo "$fruit"

# ‎printf "%s\n" "$fruit"‎ به طور کلی‌تر‎
# اما فعلاً آن را ساده نگه می‌داریم

یا، می‌توانیم این بسط‌ها را به عنوان بخشی از عبارت بزرگتری به کار ببریم:

echo "I like to eat $fruit"

اگر بخواهیم یک s در انتهای محتوای متغیر خود بگذاریم، وضع دشواری را ایجاد می‌کنیم:

echo "I like to eat $fruits"

این فرمان تلاش می‌کند، به جای متغیری با نام fruit، متغیری به نام fruits را بسط بدهد. لازم است ما به پوسته بگوییم که ما یک نام متغیر داریم که با یک گروه حروف دیگر که بخشی از نام متغیر نیستند دنبال می‌شود. می‌توانیم این کار را به این شکل انجام بدهیم:

echo "I like to eat ${fruit}s"

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

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

طول یک رشته

این یکی آسان است، بنابراین با اولین روش از آن خارج می‌شویم.

echo "The string <$var> is ${#var} characters long."

توجه نمایید که از ‎bash 3.0‎ به بعد، در مناطق چند بایتی در واقع کاراکترها هستند که به جای بایت‌ها یک تفاوت با اهمیت می‌باشند. اگر شما تعداد بایت‌ها را احتیاج دارید، لازم است ‎LC_ALL=C‎ را قبل از بسط ‎${#var}‎ صادر نمایید.

بررسی زیررشته‌ها

این مورد با پرسش و پاسخ شماره 41 همپوشانی دارد، اما در اینجا ما آن را تکرار می‌کنیم. برای بررسی یک زیررشته(شناخته، ثابت) و اقدام بر اساس وجود یا غیبت آن، فقط این کار را انجام دهید:

if [[ $var = *substring* ]]; then
  echo "<$var> contains <substring>"
else
  echo "<$var> does not contain <substring>"
fi

اگر زیررشته‌ای که شما می‌خواهید آن را جستجو کنید در یک متغیر است، و شما می‌خواهید از رفتار کردن با آن به عنوان یک glob پیش‌گیری نمایید، می‌توانید آن قسمت را نقل‌قولی کنید:

if [[ $var = *"$substring"* ]]; then
# باشد glob با زیررشته به عنوان رشته لفظی رفتار می‌شود، حتی اگر شامل کاراکترهای

اگر می‌خواهید با آن به عنوان الگوی glob رفتار شود، نقل‌قولها را حذف کنید:

if [[ $var = *$substring* ]]; then
# رفتار می‌شود glob با زیررشته به عنوان 

همچنین یک قابلیت عبارت منظم، در برگیرنده عملگر ‎=~‎ وجود دارد. به واسطه سازگاری با تمام نگارشهای Bash از ‎ 3.0‎ به بالا، مطمئن شوید برای قرار دادن عبارت منظم در یک متغیر -- آنرا به طور مستقیم در فرمان ‎[[‎ قرار نمی‌دهید. و همچنین آنرا نقل‌قولی نکنید -- وگرنه با آن به عنوان رشته لفظی رفتار می‌شود.

my_re='^fo+.*bar'
if [[ $var =~ $my_re ]]; then
#  رفتار می‌شود(ERE)به عنوان یک عبارت منظم توسعه‌یافته my_re با 

جایگزینی بخشی از یک رشته

یک نیاز رایج، تعویض بخشی از یک رشته با یک مورد دیگر می‌باشد. (اجازه بدهید در حال حاضر کلمات old و new را برای نامیدن این بخشها به کار ببریم.) اگر ما می‌دانیم کلمه old چیست، و کلمه new چه باید باشد، اما لزوماً نمی‌دانیم در کجای رشته ظاهر می‌شود، آنوقت می‌توانیم این کد را به کار ببریم:

$ var="She favors the bold.  That's cold."
$ echo "${var/old/new}"
She favors the bnew.  That's cold.

این کد فقط اولین مورد کلمه old را تعویض می‌کند. اگر می‌خواهیم تمام مواردی را که کلمه وجود دارد، تعویض کنیم، اولین کاراکتر/ را دوگانه می‌کنیم:

$ var="She favors the bold.  That's cold."
$ echo "${var//old/new}"
She favors the bnew.  That's cnew.

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

$ var="She favors the bold.  That's cold."
$ echo "${var//b??d/mold}"
She favors the mold.  That's cold.

همچنین می‌توانیم کلمه‌ای که در جستجوی آن می‌باشیم را به ابتدا یا انتهای رشته پیوند کنیم. به بیان دیگر، می‌توانیم به bash بگوییم که، در عوض جایی در وسط، فقط درصورتی که کلمه را در ابتدا یا انتهای رشته پیدا نماید، تعویض کند.

$ var="She favors the bold.  That's cold."
$ echo "${var/#bold/mold}"
She favors the bold.  That's cold.
$ echo "${var/#She/He}"
He favors the bold.  That's cold.
$ echo "${var/%cold/awful}"
She favors the bold.  That's cold.
$ echo "${var/%cold?/awful}"
She favors the bold.  That's awful

توجه نمایید که در اولین دستور اتفاقی رخ نمی‌دهد، زیرا bold در ابتدای رشته ظاهر نشده است و در فرمان سوم نیز به علت آنکه cold در انتهای رشته ظاهر نشده است. عملگر # الگو(کلمه ساده یا glob) را به ابتدای رشته پیوند می‌کند، و عملگر % به انتها پیوند می‌دهد. در فرمان چهارم، الگوی ‎cold?‎ با کلمه ‎cold.‎ (شامل نقطه) در انتهای رشته مطابقت می‌نماید.

حذف قسمتی از یک رشته

اگر بخواهیم، می‌توانیم از ترکیب دستوری ‎${var/old/}‎ یا ‎${var//old/}‎ برای تعویض کلمه با هیچ استفاده کنیم. این یک روش برای حذف بخشی از یک رشته است. اما چند روش دیگر وجود دارد که اغلب سودمندتر از آنست که شما بتوانید حدس بزنید.

اولین روش حذف چیزی از ابتدای یک رشته را در بر می‌گیرد. بار دیگر، بخشی که ما می‌خواهیم حذف کنیم می‌تواند رشته ثابتی باشد که پیشاپیش می‌شناسیم، یا شاید موردی باشد که باید با یک الگوی glob شرح بدهیم.

$ var="/usr/local/bin/tcpserver"
$ echo "${var##*/}"
tcpserver

عملگرهای ## به معنی «حذف بزرگترین رشته مورد انطباق از ابتدای محتوای متغیر» است. بخش ‎*/‎ الگویی است که می‌خواهیم مطابقت دهیم -- هر تعداد کاراکتری که به یک کاراکتر لفظی / ختم می‌شوند. اساساً نتیجه همانند نتیجه فرمان basename می‌باشد، با یک استثناء قابل اشاره: اگر رشته به یک(یا چند) کاراکتر/ ختم شود، فرمان basename نام آخرین جزء مسیر را باز خواهد گرداند، در حالیکه مورد فوق یک رشته تهی برمی‌گرداند. با هشیاری به کار ببرید.

اگر ما فقط از یک عملگر # استفاده کنیم، آنوقت کوتاهترین رشتهِ تطبیقِ محتمل را حذف می‌کنیم. این کار کمتر لازم می‌گردد، بنابراین در حال حاضر از مثال برای آن عبور می‌کنیم و بعداً یک مثال حقیقتاً خیلی خوب ارائه می‌کنیم.

به طوری که شاید حدس زده‌اید، ما می‌توانیم یک رشته را از انتهای محتوای متغیر خودمان حذف نماییم. برای مثال، با تقلید از فرمان dirname، می‌توانیم آنچه با آخرین / شروع می‌شود را حذف می‌کنیم:

$ var="/usr/local/bin/tcpserver"
$ echo "${var%/*}"
/usr/local/bin

عملگر % در اینجا به معنی «حذف کوتاهترین موردِ تطبیقِ محتمل از انتهای محتوای متغیر» می‌باشد، و ‎/*‎ الگوی glob است که با یک کاراکتر / لفظی که هر تعداد کاراکتر آن را دنبال کند، شروع می‌شود. چون ما کوتاهترین انطباق را لازم داریم، bash مجاز نمی‌شود که ‎/bin/tcpserver‎ یا هر مورد دیگری که شامل چند / می‌شود را تطبیق بدهد. فقط قسمت ‎/tcpserver‎ را حذف نموده است.

بعلاوه، عملگر%% به معنی «حذف بلندترین مورد انطباق محتمل از انتهای محتوای متغیر» می‌باشد.

اکنون بیاید مورد دشوارتری را امتحان کنیم: اگر به جای فقط آخرین قسمت، نوعی basename دوگانه -- آخرین دو قسمت یک نام مسیر -- را می‌خواستیم چه؟

$ var=/home/someuser/projects/q/quark
$ tmp=${var%/*/*}
$ echo "${var#$tmp/}"
q/quark

این یک مقدار مهارت‌آمیز است، این هم چگونگی کارکرد آن:

  • جستجوی کوتاهترین رشته تطبیق محتمل /*/* در انتهای نام مسیر. در این حالت، بر ‎/q/quark‎ باید منطبق گردد.

  • حذف آن از انتهای رشته اصلی. نتیجه این کار چیزی است که ما نمی‌خواهیم. این نتیجه را در tmp ذخیره می‌کنیم.

  • حذف آنچه نمی‌خواهیم(به اضافه یک / اضافی) از متغیر اصلی.

  • ما دو قسمت آخر نام مسیر را باقی گذاشته‌ایم.

همچنین سزاوار اشاره نمودن است، که چنانچه ما دقیقاً تشریح نموده‌ایم، الگوی مورد نظر برای حذف شدن(بعد از # یا % یا ## یا %%) نباید ثابت باشد -- می‌تواند یک جایگزینی دیگر باشد. این مورد در زندگی واقعی رایجترین حالت نیست، اما گاهی اوقات قابل استفاده است.

استخراج قسمت‌هایی از رشته‌ها

همچنین، می‌توانیم عملگرهای # و % را برای فراهم نمودن بعضی نتایج جالب‌توجه باهم ترکیب کنیم. برای مثال، ممکن بود ما بدانیم که متغیرمان شامل چیزی در میان قلابهای گوشه‌دار(کروشه)، در جایی با مقداری داده‌های ناخواسته در اطراف آن می‌باشد. می توانیم از این کد برای استخراج قسمتی که می‌خواهیم استفاده کنیم:

$ var='garbage in [42] garbage out'
$ tmp=${var##*[}
$ echo "${tmp%%]*}"
42

توجه نمایید که ما یک متغیر موقت برای نگهداری نتایج یک بسط پارامتر به کار برده‌ایم، و سپس آن نتیجه را به بسط دوم تغذیه نموده‌ایم. ما نمی‌توانیم در یک نوبت دو بسط پارامتر را در یک متغیر انجام بدهیم(واقعاً ترکیب دستوری آن را اجازه نمی‌دهد).

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

$ var='garbage in "42" garbage out'
$ tmp=${var#*\"}
$ echo "${tmp%\"*}"
42

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

var='CONFIG  .SYS'
left=${var:0:8}
right=${var:(-3)}

در اینجا، ورودی یک نام فایل ‎MS-DOS‎ به صورت ‎"8.3"‎ است، پُر شده با فاصله برای طول کامل. اگر به دلایلی نیاز داشته باشیم آن را به دو قسمتش تفکیک کنیم، چند روش محتمل برای انجام آن داریم. می‌توانستیم نام را از محل نقطه به فیلدها تفکیک نماییم(این رویکرد را بعداً نشان می‌دهیم). یا می‌توانستیم ‎${var#*.}‎ را برای به دست آوردن پسوند(قسمت بعد از نقطه) و ‎${var%.*}‎ را برای به دست آوردن قسمت سمت چپ به کار ببریم. یا می‌توانستیم ستونها را چنانکه در اینجا نشان دادیم، بشماریم.

در مثال ‎${var:0:8}‎، عدد 0 محل شروع است(0 اولین ستون است) و عدد 8 طول قسمتی که ما می‌خواهیم. اگر طول را از قلم بیاندازیم، یا اگر طول بزرگتر از باقیمانده رشته باشد، آنوقت باقیمانده رشته را به عنوان خروجی به دست می‌آوریم. در مثال ‎${var:(-3)}‎، طول را از قلم انداخته‌ایم. ما محل شروع را ‎-3‎(منفی سه) تعیین کرده‌ایم، که یعنی سه از انتها. ما باید برای اجتناب از ناسازگاری گرامری( بعداً این مورد را بحث خواهیم نمود) از پرانتزها یا فاصله بین : و عدد منفی استفاده نماییم. همچنین می‌توانستیم در این حالت از ‎${var:8}‎ برای به دست آوردن باقیمانده رشته که از ستون 8 شروع می‌شود(که نهمین ستون است) استفاده کنیم، چون می‌دانیم که طول ثابت است، اما در بسیاری موقعیت‌ها نمی‌توانیم از قبل طول را بدانیم، و مشخص نمودن عدد منفی برای محل شروع به شما اجازه می‌دهد از مقداری کار غیر ضروری پرهیز کنید.

موقعی که به هیچ وجه جداکننده‌ای میان بخش‌هایی که می‌خواهیم وجود ندارد، شمارش ستونها حتی یک تکنیک قوی‌تر است:

var='CONFIG  SYS'
left=${var:0:8}
right=${var:8}

در اینجا ما نمی‌توانیم از ‎${var#*.}‎ یا روش مشابهی استفاده کنیم!

تفکیک یک رشته به فیلدها

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

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

var=192.168.1.3
IFS=. read -r -a octets <<< "$var"

در اینجا دیگر ما به هیچ وجه در محدوده بسط پارامتر نیستسم. چندین ویژگی را با هم ترکیب کرده‌ایم:

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

  • نشانه گذاری ‎var=value command‎ به معنای آنست که ما می‌خواهیم فقط متغیر را برای اجرای این فرمان منفرد تنظیم کنیم. متغیر IFS ، وقتی read به پایان می‌رسد به همان حالتی که از قبل بود برمی‌گردد.

  • read نتایجش را در یک آرایه به نام octets قرار می‌دهد.

  • <<< "$var"‎ یعنی ما می‌خواهیم محتویات var را به عنوان ورودی استاندارد برای فرمان read استفاده کنیم.

بعد از این فرمان، نتیجه یک آرایه به نام octets می‌باشد که اولین عنصر آن (عضو شماره 0) عدد 192 است، و دومین عنصر آن(عضو شماره 1) عدد 168 است، و به همین ترتیب. اگر ما بخواهیم به جای آرایه از یک مجموعه متغیر ثابت استفاده کنیم، آن را هم می‌توانیم انجام بدهیم:

IFS=, read lastname firstname rest <<< "$name"

همچنین می‌توانیم از فیلدهایی که نمی‌خواهیم، با تخصیص آنها به متغیرهایی از قبیل، x یا junk که برای ما ارزش ندارند، یا به _ که با هر دستور رونویسی می‌شود، عبور کنیم:

while IFS=: read user x uid gid x home shell; do
 ...
done < /etc/passwd

(به جهت قابلیت حمل، بهتر است از _ اجتناب شود چون این متغیر در برخی پوسته‌ها یک متغیر فقط خواندنی می‌باشد)

یک رویکرد دیگر برای همان نوع مشکل، مستلزم استفاده تعمدی از تفکیک کلمه برای بازیابی فیلدها به صورت یک به یک می‌باشد. این روش از رویکرد آرایه که کمی قبل‌تر دیدیم قدرتمندتر نیست، اما دو مزیت دارد:

  • در پوسته sh به همان خوبی bash کار می‌کند.

  • اندکی ساده‌تر است.

var=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
found=no
set -f
IFS=:
for dir in $var
do
  if test -x "$dir"/foo; then found=yes; fi
done
set +f; unset IFS

این مثال مشابه موردی در پرسش و پاسخ شماره 81 است. Bash برای تعیین آنکه آیا یک فرمان در PATH شما وجود دارد، روشهای مناسب‌تری ارائه می‌نماید، اما این مثال، اندیشه کلی را به طور کاملاً واضح تشریح می‌کند. نکات قابل توجه:

  • set -f‎ بسط glob را غیر فعال می‌کند. شما همیشه موقعی که بسط پارامتر غیرنقل‌قولی به کار می‌برید، باید globها را غیر فعال نمایید، مگراینکه مخصوصاً بخواهید در محتوای پارامتر globها را اجازه بدهید.

  • ما از ‎set +f‎ و ‎unset IFS‎ در انتهای کُد برای باز گرداندن پوسته به حالت پیش فرض استفاده نموده‌ایم. اگرچه، لزوماً این حالتی نیست که موقع شروع کد، پوسته در آن حالت بوده. بازگرداندن پوسته به حالت قبلی آن(احتمالاً غیر پیش‌فرض) در اکثر موقعیت‌ها آزار دهنده‌تر از آنست که ارزش آن را داشته باشد، بنابراین ما در اینجا به طور فراگیر در مورد آن بحث نمی‌کنیم.

  • ازطرف دیگر، IFS محتوی لیستی از جداکننده‌های فیلد می‌باشد. ما می‌خواهیم پارمتر خود را از محل هر کاراکتر کولن تفکیک نماییم.

اگر جداکننده فیلد شما یک رشته چندکاراکتری است، آنوقت متأسفانه bash روشهای ساده‌ای برای کار با آن ارائه نمی‌کند. به جای آن بهترین گزینهِ شما، انجام آن در awk می‌باشد.

$ cat inputfile
apple::0.75::21
banana::0.50::43
cherry::0.15::107
date::0.30::20
$ awk -F '::' '{print $1 " qty " $3 " @" $2 " = " $2*$3; total+=$2*$3} END {print "Total: " total}' inputfile
apple qty 21 @0.75 = 15.75
banana qty 43 @0.50 = 21.5
cherry qty 107 @0.15 = 16.05
date qty 20 @0.30 = 6
Total: 59.3

در awk گزینه ‎-F‎ تعیین یک جداکننده فیلد با هر طولی را برای ما میسر می‌سازد. awk همچنین محاسبه ممیز شناور، آرایه‌های انجمنی ، و تنوع گسترده‌ای از ویژگی‌های دیگری را که بسیاری از پوسته‌ها فاقد آنها هستند، مجاز می‌داند.

ادغام فیلدها با یکدیگر

ساده‌ترین روش برای الحاق نمودن کمیت‌ها، استفاده آنها همراه یکدیگر، بدون چیزی مابین آنها می‌باشد:

echo "$foo$bar"

اگر ما به جای مجموعه ثابتی از متغیرها، یک آرایه داریم، آنوقت می‌توانیم آرایه را با یک کاراکتر منفرد(یا هیچ کاراکتری) میان فیلدهایش با استفاده از IFS چاپ کنیم:

$ array=(1 2 3)
$ (IFS=/; echo "${array[*]}")
1/2/3

نکات قابل توجه در اینجا:

  • نمی‌توانیم از ‎IFS=/ echo ...‎ استفاده کنیم، به علت چگونگی عملکرد تفکیک کننده.

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

  • اگر IFS برقرار نباشد، یک فاصله بین عناصر خواهیم داشت. اگر به یک رشته تهی تنظیم باشد، چیزی میان عناصر وجود نخواهد داشت.

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

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

array=(1 2 3)
first=1
for element in "${array[@]}"; do
  if ((! first)); then printf "::"; fi
  printf "%s" "$element"
  first=0
done
echo

مثال بعد، از حلقه ضمنی printf برای چاپ تمام شناسه‌های اسکریپت همراه با قلابهای زاویه‌دار در اطراف هر یک، استفاده می‌کند:

#!/bin/sh
printf "$# args:"
printf " <%s>" "$@"
echo

یک آرایه نام گذاری شده نیز می‌تواند به جای @ به کار برود(به عنوان مثال ‎"${array[@]}"‎ به تمام عناصر array بسط داده می‌شود).

اگر می‌خواهیم به جای نسخه‌برداری رشته‌ها، آنها را به یک متغیر دیگر متصل کنیم، آنوقت چند انتخاب داریم:

  • یک رشته با استفاده از ‎var="$var$newthing"‎ ‏(قابل حمل) یا ‎var+=$newthing‎ ‏( در ‏‎bash 3.1‎‏) می‌تواند در یک مرحله با یک قطعه تجمیع بشود. برای مثال:

    output=$1; shift
    while (($#)); do output+="::$1"; shift; done
  • اگر اتصال با یک فرمان منفرد printf بتواند انجام گردد، با استفاده از ‎printf -v var FORMAT FIELDS...‎ می‌تواند به یک متغیر تخصیص یابد(‎bash 3.1‎). برای مثال:

    printf -v output "%s::" "$@"
    output=${output%::}    # از بین بردن جداکننده غیرضرور از انتهای رشته
  • اگر اتصال به چند فرمان نیاز دارد، و توسعه دادن تدریجی رشته مطلوب نیست، جایگزینی فرمان می‌تواند برای تخصیص خروجی تابع به کار برود: ‎var=$(myjoinfunction)‎. این مورد می‌تواند با مقدار قابل توجهی از فرمانها نیز به کار برود:

    var=$(
      command
      command
    )
  • اشکال جایگزینی فرمان آنست که تمام سطر جدید‌های انتهایی را رها می‌کند. برای راهکار عبور موقت از این مشکل صفحه جایگزینی فرمان را ببینید.

مقادیر پیش فرض یا جایگزین

قدیمی‌ترین ویژگی‌های بسط پارامتر(هر پوسته خانواده Bourne دارای شکل اصلی این ویژگیها می‌باشد) استفاده یا اختصاص مقادیر پیش‌فرض در صورت تنظیم نبودن یک پارامتر را شامل می‌گردد. اینها نسبتاً سرراست هستند:

"${EDITOR-vi}" "$filename"

اگر متغیر EDITOR تنظیم نباشد، از vi استفاده می‌شود. گونه دیگری از این بسط وجود دارد:

"${EDITOR:-vi}" "$filename"

این بسط در صورتی از vi استفاده می‌کند که متغیر EDITOR تنظیم نباشد یا تهی باشد. سابقاً به یک ترکیب دستوری نابجایی اشاره نمودیم که نیازمند پرانتزها یا فاصله برای کارکردن بود:

var='a bunch of junk089'
value=${var:(-3)}

اگر در اینجا از ‎${var:-3}‎ استفاده می‌کردیم، به عنوان «استفاده از 3 برای پیش فرض var در صورتی که تنظیم نباشد»، تفسیر می‌گردید، زیرا ترکیب دستوری اخیر قبل از اینکه bash وجود داشته باشد، مورد استفاده بوده است. از اینرو نیاز به یک راه حل عبور از آن می‌باشد.

همچنین اگر متغیر از قبل تنظیم نگردیده باشد، می‌توانیم مقدار پیش‌فرضی را به آن تخصیص بدهیم:

: "${PATH=/usr/bin:/bin}"
: "${PATH:=/usr/bin:/bin}"

در اولی اگر PATH برقرار باشد، اتفاقی رخ نمی‌دهد. اگر تنظیم نباشد، آنوقت مقدار ‎/usr/bin:/bin‎ به آن اختصاص داده می‌شود. در دستور تخصیص دوم، اگر PATH به یک مقدار تهی نیز تنظیم شده باشد، تخصیص انجام می‌شود. چون ‎${...}‎ یک عبارت است و فرمان نیست، باید در یک فرمان به کار برود. به طور سنتی، فرمان : (که هیچ کاری انجام نمی‌دهد، و یک فرمان داخلی حتی در اکثر پوسته‌های قدیمی است) برای این منظور به کار می‌رود.

سرانجام، ما این عبارت را داریم:

${var+foo}

این یک به معنی آنست که، اگر متغیر تنظیم باشد foo به کار برود و در غیر اینصورت، هیچ . این یک بررسی شرطی بینهایت ابتدایی است ، و دو مورد استفاده اصلی دارد:

  • عبارت ‎${1+"$@"}‎ برای عبور موقت از رفتار ناموفق ‎"$@"‎ در پوسته‌های قدیمی یا دارای باگ، موقع نوشتن یک WrapperScript می‌باشد.

  • یک بررسی از قبیل ‎if test "${var+defined}"‎ می‌تواند برای تعیین آنکه آیا یک متغییر تنظیم شده است به کار برود.

  • برای عبور دادن شناسه‌های اختیاری مانند: ‎cmd ${opt_x+-x "$opt_x"} ...

تقریباً هرگز خارج از این سه زمینه استفاده نمی‌شود.

See Also

بسط پارامتر (نگارش موجز، با جدول‌های مفید).


CategoryShell

پرسش و پاسخ 100 (آخرین ‎2013-07-24 16:20:42‎ توسط 188-223-3-27)


جدیدترین یا قدیمی‌ترین فایل در دایرکتوری


چطور می‌توانم جدیدترین(یا قدیمی‌ترین) فایل یک شاخه را به دست آورم؟

این صفحه باید با پرسش و پاسخ شماره ۳ ادغام می‌شد.

پاسخ ‎ls -t | head -1‎ که فوراً به ذهن می‌رسد، اشتباه است، به علت آنکه تجزیه خروجی ls نا امن است، در عوض، باید حلقه‌ای ایجاد نموده و نشانه‌های زمان را مقایسه کنید:

# Bash
files=(*) newest=${files[0]}
for f in "${files[@]}"; do
  if [[ $f -nt $newest ]]; then
    newest=$f
  fi
done

آنوقت جدیدترین فایل(براساس زمان ویرایش فایل) را در ‎$newest‎ خواهید داشت. برای به دست آوردن قدیمی‌ترین فایل، به سادگی ‎-nt‎ را به ‎-ot‎ تغییر بدهید(برای لیستی از عملگرها ‎help test‎ را ببینید)، و البته برای اجتناب از تداخل، نام متغیرها را تغییر بدهید.

Bash وسیله‌ای غیر از mtime برای مقایسه نشانه‌های زمان فایل ندارد، بنابراین اگر بخواهید(برای مثال) آخرین فایل دستیابی شده (جدیدترین توسط atime) را به دست بیاورید، می‌باید از فرمان خارجی ‎stat(1)‎ (اگر آن را دارید) یا از فرمان finfo داخلی قابل بارگیری (اگر می‌توانید دستورات داخلی را بارگیری کنید) کمک بگیرید.

این هم مثالی از کاربرد stat از ‎coreutils 6.10‎ گنو (متأسفانه، حتی در میان سیستم‌های لینوکس، ترکیب دستوری فرمان stat سازگار نیست) برای به دست آوردن آخرین فایل دستیابی شده. ( در این نگارش، ‎%X‎ آخرین زمان دستیابی است.)

# Bash, GNU coreutils
newest= newest_t=0
for f in *; do
  t=$(stat --format=%X -- "$f")   # atime
  if ((t > newest_t)); then
    newest_t=$t
    newest=$f
  fi
done

این روش همچنین دارای اشکال تولید مثل یک برنامه خارجی برای هر فایل داخل دایرکتوری می‌باشد، پس، این روش فقط در صورت ضرورت باید به کار برود. برای به دست آوردن قدیمی‌ترین فایل با استفاده از این روش، یا باید ‎oldest_t‎ را با بیشترین مقدارِ ممکنِ نشانه زمان(گزاره مهارت‌آمیز، مخصوصاً چنانکه ما سال 2038 را پیشنهاد می‌کنیم)، یا با نشانه زمان اولین فایل در دایرکتوری، چنانکه در مثال اول دیدیم، مقداردهی کنید.

این هم یک راه‌حل دیگر با تولیدمثل یک برنامه خارجی، اما موافق با posix:

# posix
unset newest
for f in ./*; do
  # در اولین دور حلقه newest تنظیم متغیر‎
  newest=${newest-$f} 
  #  در find برای اجتناب از سیر نزولی در دایرکتوری‌ها می‌باشد، وضعیت خروج ‎-prune‎‎
  #  .اینجا بیفایده است، ما خروجی را بررسی می‌کنیم 
  if [ "$(find "$f" -prune -newer "$newest")" ]; then 
    newest=$f
  fi
done

مثال: چگونه غیر از آخرین دایرکتوری، همگی را حذف کنیم. (توجه، زمان ویرایش دایرکتوری زمان آخرین عملیاتی است که در آن دایرکتوری تغییر ایجاد می‌کند -- یعنی آخرین ایجاد، حذف، یا تغییرنام فایل در آن.)

 $ cat clean-old 
 dirs=(enginecrap/*/) newest=${dirs[0]}
 for d in "${dirs[@]}"
  do if [[ $d -nt $newest ]]
     then newest=$d
     fi
  done

 for z in "${dirs[@]}"
  do if [[ "$z" != "$newest" ]]
     then rm -rf "$z"
     fi
  done
 $ for x in 20101022 20101023 200101025 20101107 20101109; do mkdir enginecrap/"$x";done
 $ ls enginecrap/
 200101025       20101022        20101023        20101107        20101109
 $ ./clean-old 
 $ ls enginecrap/
 20101109


CategoryShell

پرسش و پاسخ 99 (آخرین ویرایش ‎2013-03-18 21:54:17‎ توسط geirha)


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


چگونه پشتیبانی محلی را به اسکریپت‌های 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)