کنترل جریان با if در اسکریپتنویسی
در دروس قبلی با یک مشکل مواجه شدیم چگونه کاری کنیم که اسکریپت ایجاد گزارش ما با دسترسیهای کاربری که در حال اجرا کردن اسکریپت هست منطبق باشد؟ راهکار این مشکل نیازمند این است که راهی برای تغییر مسیرها در اسکریپت خود بر اساس نتایج یک تست پیدا کنیم. در اصطلاح برنامه نویسی ما نیاز داریم تا برنامه منشعب شود.
یک مثال ساده را فرض کنید:
X = 5
If X = 5, then:
Say “X equals 5.”
Otherwise:
Say “X is not equal to 5.”
این مثال یک نمونه از انشعاب کد است که در آن بیان میشود که در موقعیت ٥ = x بگو که X برابر ۵ است و در غیر اینصورت بگو که X برابر ۵ نیست در این درس به نحوه انشعاب کد بوسیله if میپردازیم.
استفاده از if
کد قبلی که در بالا آوردیم را در شل (Shell) میتوانیم به صورت زیر بنویسیم:
x=5
if [ $x = 5 ]; then
echo "x equals 5."
else
echo "x does not equal 5."
fi
یا اینکه میتوانیم آن را به صورت مستقیم در خط فرمان وارد کنیم:
[me@linuxbox ~]$ x=5 [me@linuxbox ~]$ if [ $x = 5 ]; then echo "equals 5"; else echo "does not equal 5"; fi equals 5 [me@linuxbox ~]$ x=0 [me@linuxbox ~]$ if [ $x = 5 ]; then echo "equals 5"; else echo "does not equal 5"; fi does not equal 5
در این مثال ما فرمان را دوبار وارد کردیم. اول با مقدار ۵ که نتیجه آن این شد که برای ما چاپ کرد مساوی با ۵ و سپس با مقدار صفر که در نتیجه برای ما چاپ کرد که ۵ نیست. ساختار if به صورت زیر است:
if commands; then
commands
[elif commands; then
commands...]
[else
commands]
fi
در این ساختار commands لیستی از فرمانها است. در ابتدا شاید برای شما کمی پیچیده و گیجکننده به نظر برسد. ولی قبل از اینکه واضح بفهمیم بایستی ببینیم که شل چگونه موفقیت یا شکست یک فرمان را ارزیابی میکند.
وضعیت خروج
فرمانها و اسکریپتهای شل به منظور پایان دادن به فرمانها مقداری با نام وضعیت خروج را ارسال میکنند. این مقدار که یک عدد صحیح بین ۰ تا ۲۵۵ هست. موفقیت یا شکست اجرای دستور را نشان میدهد. بر اساس قرار داد. مقدار صفر نشاندهنده موفقیت و دیگر مقادیر نشان دهنده شکست هستند. همچنین شل یک پارامتر را که میتوانیم از آن به منظور آزمون وضیعت خروج استفاده کنیم فراهم میکند. در مثال زیر میبینید:
[me@linuxbox ~]$ ls -d /usr/bin /usr/bin [me@linuxbox ~]$ echo $? 0 [me@linuxbox ~]$ ls -d /bin/usr ls: cannot access /bin/usr: No such file or directory [me@linuxbox ~]$ echo $? 2
در این مثال، ما فرمان ls را دو مرتبه اجرا کردیم. بار اول دستور با موفقیت اجرا شد. در نتیجه اگر مقدار پارامتر $? را ببینیم صفر است. برای بار دوم که فرمان را اجرا میکنیم با خطا مواجه میشویم و دوباره که مقدار پارامتر $? را آزمایش کنیم. مقدار ۲ را میبینیم که نشاندهنده این است که فرمان با خطا مواجه شده است. برخی فرمانها برای عیبیابی خطاها از وضعیت خروج متفاوتی استفاده میکنند. در حالیکه بسیاری دیگر در صورت خطا مقدار یک را نشان میدهند. صفحات راهنما اغلب حاوی بخشی با نام Exit Status یا همان وضعیت خروج است که بیان میکند چه کدهایی استفاده شده است. هر چند که مقدار صفر همیشه نشاندهنده موفقیت است. شل (Shadi) دو فرما درون ساخت بسیار ساده را ارایه میکند که به غیر از پایان دادن به فرمانها با مقادیر وضعیت خروج صفر و یک کار دیگری را انجام نمیدهد. فرمان true همیشه با موفقیت اجرا میشود و فرمان false همیشه بدون موفقیت اجرا میشود:
[me@linuxbox ~]$ true [me@linuxbox ~]$ echo $? 0 [me@linuxbox ~]$ false [me@linuxbox ~]$ echo $? 1
ما میتوانیم از این فرمانها برای مشاهده اینکه چگونه عبارت if کار میکند استفاده کنیم. در حقیقت کاری که فرمان if میکند این است که موفقیت یا شکست فرمانها را ارزیابی میکند:
[me@linuxbox ~]$ if true; then echo "It's true."; fi It's true. [me@linuxbox ~]$ if false; then echo "It's true."; fi [me@linuxbox ~]$
فرمان echo “It’s true.” زمانی اجرا میشود که if قبل از آن فرمان با موفقیت اجرا شود و در صورتیکه فرمان if با موفقیت اجرا نشود echo نیز اجرا نخواهد شد. اگر لیستی از فرمانها به دنبال if بیاید آخرین فرمان ارزیابی خواهد شد.
[me@linuxbox ~]$ if false; true; then echo "It's true."; fi It's true. [me@linuxbox ~]$ if true; false; then echo "It's true."; fi [me@linuxbox ~]$
استفاده از تست (Test)
تاکنون رایج ترین فرمان مورد استفاده برای if فرمان test بوده است. test انوع مختلفی از بررسیها و مقایسهها را انجام میدهد. این فرمان دارای دو شکل برابر است:
test expression
and the more popular
[ expression ]
در حالی که در ساختارهای بالا expression عبارتی است که درست یا غلط بودن آن ارزیابی میشود. فرمان test وضعیت خروج صفر را در صورت درست بودن و وضعیت خروج یک را زمانی غلط بودن عبارت برمیگرداند.
عبارات فایل (File Expressions)
عبارات موجود در جدول زیر به منظور ارزیابی وضعیتهای فایلها استفاده میشود:
| عبارت | درست است اگر |
|---|---|
| file1 -ef file2 | file1 و file2 شماره اینودهای یکسانی داشته باشند (هر دو فایل یک نام با یک لینک سخت اشاره کنند) |
| file1 -nt file2 | file1 جدیدتر از file2 باشد |
| file1 -ot file2 | file1 قدیمیتر از file2 میباشد. |
| -b file | file موجود باشد و یک فایل بلوک ویژه (دیوایس) باشد |
| -c file | file موجود باشد و یک فایل کاراکتر ویژه (دیوایس) باشد |
| -d file | file موجود باشد و یک پوشه باشد |
| -e file | file موجود باشد |
| -f file | file موجود باشد و یک فایل عادی باشد |
| -g file | فایل موجود باشد و یک مجموعه شناسه گروه باشد set group ID |
| -G file | فایل موجود باشد و بوسیله effective group ID مالکیت شود |
| -k file | فایل موجود باشد و یک مجموعه sticky bit داشته باشد |
| -L file | فایل موجود باشد و یک لینک سمبولیک باشد |
| -O file | فایل موجود باشد و بوسیله یک effective user ID مالکیت شود |
| -p file | فایل موجود باشد و یک پایپ نامگذاری شده اشد |
| -r file | فایل موجود باشد و خواندنی باشد (دارای مجوزهای دسترسی خواندن برای کاربر قابل اجرا میباشد) |
| -s file | فایل موجود باشد و طول آن بیشتر از صفر است |
| -S file | فایل موجود باشد و یک سوکت شبکه باشد |
| -t fd | fd یک توصیفکننده مستقیم به ترمینال میباشد. میتوان از آن برای تشخیص خطاهای ورودی و خروجی هدایت شده استفاده کرد. |
| -u file | فایل موجود باشد و شناسه کاربر تنظیم شده باشد |
| -w file | فایل موجود باشد و نوشتنی باشد |
| -x file | فایل موجود باشد و اجرایی باشد |
در اینجا ما یک اسکریپت را داریم که برخی از عبارتهای فایل را شرح میدهد:
#!/bin/bash
# test-file: Evaluate the status of a file
FILE=~/.bashrc
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
if [ -d "$FILE" ]; then
echo "$FILE is a directory."
fi
if [ -r "$FILE" ]; then
echo "$FILE is readable."
fi
if [ -w "$FILE" ]; then
echo "$FILE is writable."
fi
if [ -x "$FILE" ]; then
echo "$FILE is executable/searchable."
fi
else
echo "$FILE does not exist"
exit 1
fi
exit
این اسکریپت قابلی که به ثابت FILE اختصاص یافته را ارزیابی کرده و نتایج آن را به صورتیکه ارزیابی انجام شده نشان میدهد. دو نکته جالب توجه درباره اسکریپت وجود دارد.
اول اینکه توجه کنید که چگونه پارامتر $FILE درون عبارات درون کوتیشن قرار گرفته. این کار ضروری نیست ولی یک نوع دفاع در مقابل خالی بودن پارامتر به شمار می رود. اگر که بسط پارامتر $FILE یک مقدار خالی را بدهد در نتیجه نبود کوتیشن باعث خطا میشود.
test_file () {
# test-file: Evaluate the status of a file
FILE=~/.bashrc
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
if [ -d "$FILE" ]; then
echo "$FILE is a directory."
fi
if [ -r "$FILE" ]; then
echo "$FILE is readable."
fi
if [ -w "$FILE" ]; then
echo "$FILE is writable."
fi
if [ -x "$FILE" ]; then
echo "$FILE is executable/searchable."
fi
else
echo "$FILE does not exist"
return 1
fi
}
دوم اینکه به وجود فرمانهای سه در خط نزدیک به پایان اسکریپت دقت کنید . فرمان exit یک آرگومان اختیاری را قبول میکند که به وضعیت خروج اسکریپت تبدیل میشود. زمانی که هیچ آرگومانی اضافه نشود. مقدار پیشفرض وضعیت خروج صفر است. با استفاده از exit به این شیوه به اسکریپت اجازه میدهیم تا شکست را نشان دهیم در صورتیکه $FILE به یک نام فایل غیر موجود بسط پیدا کند. فرمان exit در خط آخر اسکریپت نیز به صورت سنتی آورده میشود. زمانی که یک اسکریپت به پایان فایل میرسد. با یک مقدار وضعیت خروج صفر به صورت پیش فرض پایان میپذیرد. به صورت مشابه توابع شل (Shell Functions) نیز میتوانند یک وضعیت خروج را با شامل کردن یک آرگومان عدد صحیح به فرمان return نشان دهند. اگر که اسکریپت بالا را به یک تابع شل به منظور قرار دادن در یک برنامه بزرگتر، تبدیل کنیم، میتوانیم فرمانهای return را جایگزین exit کنیم تا نتیجه دلخواه بدست آوریم.
عبارات رشته (String Expressions)
عباراتی که در جدول زیر استفاده شدهاند بهمنظور ارزیابی رشتهها بهکار میروند:
| صحیح است اگر | عبارت |
|---|---|
| رشته نال نیست | string |
| طول رشته بزرگتر از صفر است | -n string |
| طول رشته صفر است | -z string |
| string1 و string2 برابرند. یک یا دو علامت مساوی استفاده میشود ولی استفاده از دو علامت مساوی ترجیح داده میشود. | string1 = string2 string1 == string2 |
| string1 و string2 برابر نیستند. | string1 != string2 |
| string1 پس از string2 مرتب میشود. | string1 > string2 |
| string1 قبل از string2 مرتب میشود. | string1 < string2 |
در اینجا یک اسکریپت را داریم که عبارات رشته را بیان میکند:
#!/bin/bash
# test-string: evaluate the value of a string
ANSWER=maybe
if [ -z "$ANSWER" ]; then
echo "There is no answer." >&2
exit 1
fi
if [ "$ANSWER" = "yes" ]; then
echo "The answer is YES."
elif [ "$ANSWER" = "no" ]; then
echo "The answer is NO."
elif [ "$ANSWER" = "maybe" ]; then
echo "The answer is MAYBE."
else
echo "The answer is UNKNOWN."
fi
در این اسکریپت ما ثابت ANSWER را ارزیابی میکنیم. ابتدا تشخیص میدهیم که رشته خالی هست یا نه. اگر خالی هست. اسکریپت را پایان میدهیم و وضعیت خروج را به مقدار یک تعیین میکنیم. به تغییر مسیری که بر روی فرمان echo انجام شده توجه کنید. این باعث میشود که پیام خطای There is no answer به خطای استاندارد هدایت شود. اگر رشته خالی نباشد. مقدار رشته را ارزیابی میکنیم تا ببینیم که برابر no یا yes یا maybe هست. این کار را با استفاده از eli که مخفف else if هست انجام میدهیم. با استفاده از elif قادریم که یک تست منطقی پیچیدهتر را ایجاد کنیم.
عبارات عدد صحیح (Integer Expressions)
عباراتی که در جدول زیر میبینید از اعداد صحیح استفاده کردهاند:
| صحیح است اگر | عبارت |
|---|---|
| integer1 برابر با integer2 میباشد. | integer1 -eq integer2 |
| integer1 برابر با integer2 نمیباشد. | integer1 -ne integer2 |
| integer1 کمتر از برابر با integer2 میباشد. | integer1 -le integer2 |
| integer1 کمتر از integer2 میباشد. | integer1 -lt integer2 |
| integer1 بزرگتر یا برابر با integer2 میباشد. | integer1 -ge integer2 |
| integer1 بزرگتر از integer2 میباشد. | integer1 -gt integer2 |
این هم یک اسکریپت که آنها را شرح میدهد:
#!/bin/bash
# test-integer: evaluate the value of an integer.
INT=-5
if [ -z "$INT" ]; then
echo "INT is empty." >&2
exit 1
fi
if [ $INT -eq 0 ]; then
echo "INT is zero."
else
if [ $INT -lt 0 ]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0 ]; then
echo "INT is even."
else
echo "INT is odd."
fi
fi
بخش جالب اسکریپت جایی است که تشخیص می دهد که یک عدد صحیح زوج است یا فرد. با انجام عملیات باقیمانده ۲ بر روی عدد. که عدد را بر دو تقسیم میکند و باقیمانده را برمیگرداند میتوانیم تشخیص دهیم که عدد فرد است یا زوج.
نسخهای به روزتر از تست (test)
نسخههای اخیر بش (Bash) دارای دستوری ترکیبی هستند که به عنوان جایگزین بهینهیافته test به کار میرود. این نسخه از ساختار دستوری زیر استفاده می کند: [[ expression ]] که در آن expression همان عبارتی است که میخواهیم درست یا غلط بودن آن را ارزیابی کنیم. فرمان [[ ]] بسیار شبیه فرمان test هست از همه عبارات آن پشتیبانی میکند ولی یک عبارت رشتهای جدید مهم را اضافه می کند:
string1 =~ regex
که اگر string1 با عبارت منظم توسعه یافته regex مطابقت داشته باشد. مقدار صحیح را برمیگرداند. این باعث میشود که فرصتهای زیادی برای انجام اعتبارسنجی بیشتر بر روی داده ممکن شود. در مثال اخیر خود برای عبارت عدد صحیح، در صورتی که اسکریپت ثابت INT را برای هر چیزی به غیر از عدد صحیح دریافت میکرد شکست میخورد. در نتیجه اسکریپت به راهی نیاز دارد تا تصدیق کنند که ثابت حاوی یک عدد صحیح است. با استفاده از عبارت [[]] به همراه عملگر عبارت رشتهای می توانیم اسکریپت را به صورت زیر بهینه کنیم:
#!/bin/bash
# test-integer2: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [ $INT -eq 0 ]; then
echo "INT is zero."
else
if [ $INT -lt 0 ]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0 ]; then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
با اجرای عبارت منظم قادر هستیم تا مقدار INT را فقط به رشته هایی که با یک علامت اختیاری (_) آغاز میشوند و به دنبال آن یک یا چند عدد می آید محدود کنیم. این عبارت همچنین امکان خالی بودن مقادیر را از بین میبرد . یکی از دیگر ویژگیهایی عبارت [[ ]] این است که عملگر == از مطابقت الگو به همان شیوه بسط نام مسیر پشتیبانی میکند. برای مثال:
[me@linuxbox ~]$ FILE=foo.bar [me@linuxbox ~]$ if [[ $FILE == foo.* ]]; then > echo "$FILE matches pattern 'foo.*'" > fi foo.bar matches pattern 'foo.*'
این باعث میشود که عبارت [[ ]] به منظور ارزیابی فایل و نامهای مسیر مفید باشد.
ترکیب (()) طراحی شده برای اعداد صحيح
علاوه بر استفاده از فرمان ترکیبی [[ ]] بش (Bash) یک فرمان ترکیبی دیگر یعنی (()) را نیز ارایه میکند که به منظور انجام عملیات بر روی اعداد صحیح مفید است. این فرمان یک مجموعه کامل از ارزیابیهای ریاضی را پشتیبانی میکند. فرمان ترکیبی (()) به منظور انجام تستهای واقعیت ریاضی (arithmetic truth tests) استفاده میشود. نتیجه یک تست واقعیت ریاضی درست است در صورتیکه نتیجه ارزیابی ریاضی غیر از صفر باشد:
[me@linuxbox ~]$ if ((1)); then echo "It is true."; fi It is true. [me@linuxbox ~]$ if ((0)); then echo "It is true."; fi [me@linuxbox ~]$
با استفاده از ترکیب (()) میتوانیم کمی فایل اسکربیت test-integer2 را سادهتر کنیم:
#!/bin/bash
# test-integer2a: evaluate the value of an integer.
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if ((INT == 0)); then
echo "INT is zero."
else
if ((INT < 0)); then
echo "INT is negative."
else
echo "INT is positive."
fi
if (( ((INT % 2)) == 0)); then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
توجه کنید که از علامتهای بزرگتر و کوچکتر استفاده کردیم و == به منظور تست برابری استفاده میشود. این یک ترکیب طبیعیتر برای کار با اعداد صحیح است. همچنین دقت کنید که به این دلیل که فرمان ترکیبی (()) به جای اینکه یک فرمان معمولی باشد. بخشی از ترکیب شل هست و فقط با اعداد صحیح کار میکنند. قادر است تا متغیرها را با نام شناسایی کند و نیازی به انجام بسط ندارد.
ترکیب عبارات
ما میتوانیم عبارات را با هم ترکیب کنیم تا ارزیابیهای پیچیدهتری ایجاد کنیم عبارات بوسیله عملگرهای منطقی باهم ترکیب میشوند. این عملگرها عبارتند از OR, AND و NOT.
test و [[ ]] از عملگرهای متفاوتی برای ارایه این عملیاتها استفاده میکنند که در جدول زیر آورده شدهاند:
| [[ ]] و (( )) | تست | عملیات |
|---|---|---|
| && | -a | AND |
| || | -o | OR |
| ! | ! | NOT |
در اینجا یک مثال از عملیات AND آورده شده است. اسکریپت زیر تشخیص میدهد که آیا یک عدد صحیح درون محدودهای از مقادیر قرار دارد یا نه:
#!/bin/bash
# test-integer3: determine if an integer is within a
# specified range of values.
MIN_VAL=1
MAX_VAL=100
INT=50
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [[ INT -ge MIN_VAL && INT -le MAX_VAL ]]; then
echo "$INT is within $MIN_VAL to $MAX_VAL."
else
echo "$INT is out of range."
fi
else
echo "INT is not an integer." >&2
exit 1
fi
در این اسکریپت تشخیص میدهیم که آیا مقدار عدد صحیح INT بین مقادیر MIN_VAL و MAX_VAL نهفته است یا نه. این کار با استفاده از ترکیب [[ ]] انجام میشود که مستلزم دو عبارت است که با عملگر && از هم جدا شدهاند. علاوه بر این میتوانیم کدی را با استفاده از test داشته باشیم:
if [ $INT -ge $MIN_VAL -a $INT -le $MAX_VAL ]; then
echo "$INT is within $MIN_VAL to $MAX_VAL."
else
echo "$INT is out of range."
fi
علاوه بر این پرانترهایی را دور عبارت برای گروه کردن آنها قرار دادهایم. اگر که این پرانتزها قرار داده نشوند. نفی فقط بر روی عبارت اول اعمال میشود و نه کل عبارت کدنویسی این عبارت یا test به شیوه زیر صورت میپذیرد:
#!/bin/bash
# test-integer4: determine if an integer is outside a
# specified range of values.
MIN_VAL=1
MAX_VAL=100
INT=50
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [[ ! (INT -ge MIN_VAL && INT -le MAX_VAL) ]]; then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
else
echo "INT is not an integer." >&2
exit 1
fi
از آنجایی که همه عبارات و عملگرهایی که بوسیله test استفاده میشوند. بوسیله شل (Sell) به عنوان آرگومانهای دستوری در نظر گرفته می@شوند (بر خلاف [[]] و (())) . کاراکترهایی که معنای خاصی در بش (Bash) دارند (مثل < و > و AND ، بایستی درون کوتیشن قرار گیرند. میبینید که ترکیب [[ ]] تقریبا کار مشابهی انجام میدهد. از کدام یک بایستی استفاده کنیم؟
test سنتی و قدیمی است دو بخشی از POSIX میباشد در حالیکه [[ ]] ویژه بش (Bash) است. این مهم است بدانید که چگونه از test استفاده کنید چونکه به صورت گسترده مورد استفاده قرار میگیرد ولی ترکیب [[ ]] بسیار مفیدتر است و کدنویسی با آن نیز راحتتر است.
عملگرهای کنترل (Control Operators)
بش (Bash) دو عملگر کنترل که قادر به انجام انشعاب هستند را ارایه میکنند. عملگرهای & (AND) و عملگر || (OR) درست شبیه عملگرهای منطقی درون فرمنانهای ترکیبی [[ ]] کار میکنند. ساختار دستوری آن به صورت زیر است : command1 && command2 و command1 || command2
مهم است که رفتار این دو را درک کنید.
در عملگر && فرمان command1 اجرا میشود و فرمان command2 فقط زمانی اجرا میشود که command1 با موفقیت اجرا شده باشد.
در عملگر || فرمان command1 اجرا میشود و فرمان command2 فقط زمانی اجرا می شود که دستور command1 ناموفقیتآمیز اجرا شده باشد. یعنی در عمل میتوانیم کاری را به صورت زیر انجام دهیم:
[me@linuxbox ~]$ mkdir temp && cd temp
همانگونه میبینید یک پوشه با نام temp ایجاد کردیم و در صورتی که ایجاد پوشه موفقیتآمیز باشد . پوشه فعلی به پوشه temp تغییر پیدا میکند. فرمان دوم فقط زمانی اجرا میشود که ایجاد پوشه با موفقیت همراه باشد. در عوض:
[me@linuxbox ~]$ [ -d temp ] || mkdir temp
موجودیت پوشه temp را آزمون میکند و فقط اگر این آزمون با شکست مواجه شود ایمنی زمانی که پوشه temp را نداشته باشیم . تلاش به ایجاد این پوشه میکند. این نوع ساختار برای بکارگیری خطاها در اسکرپیت بسیار مفید است. برای مثال میتوانیم اسکریپت زیر را اجرا کنیم:
[ -d temp ] || exit 1
اگر که اسکریپت نیاز به پوشه temp داشته باشد. و این پوشه وجود نداشته باشد. سپس به اسکریپت با یک وضعیت خروج ۱ پایان میدهد.
درباره فرشید نوتاش حقیقت
همیشه نیازمند یک منبع آموزشی فارسی در حوزه نرمافزارهای آزاد/ متنباز و سیستمعامل گنو/لینوکس بودم. از این رو این رسالت رو برای خودم تعریف کردم تا رسانه «محتوای باز» رو بوجود بیارم.
نوشتههای بیشتر از فرشید نوتاش حقیقتاین سایت از اکیسمت برای کاهش جفنگ استفاده میکند. درباره چگونگی پردازش دادههای دیدگاه خود بیشتر بدانید.
دیدگاهتان را بنویسید