نمی توانید. مستعارها در bash بینهایت ابتدایی هستند، و در حقیقت برای هیچ مقصود مهمی مناسب نمیباشند. حتی صفحه مستندات bash به طور صریح میگوید:
به جای آن از تابع استفاده کنید. برای مثال:
settitle() { case $TERM in *xterm*|*rxvt*) echo -en "\e]2;$1\a";; esac; }
اوه، در ضمن: مستعارها در اسکریپتها مجاز نیستند. آنها فقط در پوستههای محاورهای مجاز میباشند، و حقیقتاً چنین است چونکه اگر آنها به طور کلی برچیده میشدند، کاربران با صدای بلند فریاد میکشیدند. اگر شما یک اسکریپت مینویسید، همیشه به جای آن از تابع استفاده کنید.
پرسش و پاسخ 80 (آخرین ویرایش 2008-11-22 23:25:45 توسط GreyCat)
این در واقع سه پرسش مختلف است، بنابراین ما پاسخ را به سه قسمت تفکیک کردهایم.
آسانترین روش برای مطابقت با سطرهایی که شامل هر دو رشته foo و bar هستند استفاده از دو فرمان grep میباشد:
grep foo | grep bar grep foo "$myfile" | grep bar # برای آنهایی که نیاز به نگهداری دستی دارند
این کار همچنین میتواند با یک egrep انجام بشود، هر چند(به طوریکه احتمالاً میتوانید حدس بزنید) این فرمان نمیتواند به طور واقعی برای سنجش بیش از دو الگو به کار برود:
egrep 'foo.*bar|bar.*foo'
اگر ترجیح میدهید، میتوانید با یک جمله sed یا awk به این مقصود نائل شوید:
sed -n '/foo/{/bar/p}' awk '/foo/ && /bar/'
اگر نیاز به راه حل سنجش awk برای تعداد اختیاری الگوها دارید، میتوانید یک سطر فرمان awk ممتد طرحریزی نمایید:
# bashو ksh93 درپوستههای # را ایجاد میکند awk "/$1/&&/$2/&&...." # دادههایی که میخواهند مطابقت شوند باید در ورودی استاندارد باشند # سطرهای انطباق یافته را در خروجی استاندارد مینویسد multimatch() { (($# < 2)) && { echo "usage: multimatch pat1 pat2 [...]" >&2; return 1; } awk "/$1/$(printf "&&/%s/" "${@:2}")" }
یا، نگارش POSIX:
# POSIX multimatch() { [ $# -lt 2 ] && { echo "usage: multimatch pat1 pat2 [...]" >&2; return 1; } __p1=$1 shift awk "/$__p1/$(printf "&&/%s/" "$@")" }
افسوس، توابع POSIX متغیرهای محلی ندارند. همچنین، در صورتیکه هر یک از الگوها شامل کاراکترهای
یک نگارش POSIX که عبارتهای منظم را در اسکریپت awk تعبیه نمیکند.
# POSIX multimatch() { awk 'BEGIN{for(i=1;i<ARGC;i++) a[i]=ARGV[i]; ARGC=1} {for (i in a) if ($0 !~ a[i]) next; print}' "$@" }
روشهای بسیار زیادی برای مطابقت سطرهای شامل foo یا bar وجود دارد. با گزینه -e میتوان الگوهای چندتایی را با grep به کار برد:
grep -e 'foo' -e 'bar'
یا میتوانید یک الگو با egrep (یا grep -E) بسازید:
egrep 'foo|bar' grep -E 'foo|bar'
(نمیتوانید ازعملگر اتصال | با grep ساده استفاده کنید. این عملگر | فقط در عبارتهای منظم توسعهیافته معتبر میباشد.)
این کار همچنین میتواند با sed، awk، و غیره انجام بشود.
awk '/foo|bar/'
رویکرد awk برتری آن را دارد که به شما اجازه استفاده از سایر ویژگیهای awk روی سطرهای انطباق یافته را میدهد.
مطابقت سطرهایی که شامل هیچ یک از رشتههای foo و bar نیستند:
grep -E -v 'foo|bar' # را ترجیح میدهند egrep -v 'foo|bar' بعضی اشخاص
اگر میخواهید مطابقت کنید که فایلها (به جای سطرها) شامل هر دو رشته foo و bar هستند، چندین رویکرد عملی وجود دارد. سادهترین(اگر چه ضرورتاً کارآمدترین نیست) دوبار خواندن فایل است:
grep -q foo "$myfile" && grep -q bar "$myfile" && echo "Found both"
راه حل استفاده دوگانه grep -q این مزیت را دارد که در هر نوبت خواندن، در جایی که مورد انطباقی پیدا شود توقف میکند، بنابراین اگر یک فایل حجیم داشته باشید، اما کلمات در ابتدای آن باشند، فقط قسمت اول فایل خوانده میشود. متأسفانه، اگر انطباقها در انتهای فایل باشند(وضعیت وخیم: در انتهاییترین سطرهای فایل) شما کل فایل را دوبار میخوانید.
رویکرد دیگری یکبار خواندن فایل، و حفظ نمودن اثر آنچه دیدهاید، در ضمن پیشروی در فایل میباشد. در awk:
awk '/foo/{a=1} /bar/{b=1} a&&b{print "both found";exit} END{if (a&&b){ exit 0} else{exit 1}}'
این کد فایل را یک بار میخواند، موقعی که هر دو الگو منطبق گردیدند توقف میکند. مهم نیست چه اتفاقی رخ میدهد، پس از آن بلوک END اجرا میشود، و مطابق آن وضعیت خروج تنظیم میگردد.
اگر میخواهید بررسیهای اضافی در محتویات فایل انجام بدهید، این راهکار awk میتواند کاملاً به آسانی وفق داده شود.
پرسش و پاسخ 79 (آخرین ویرایش 2011-04-15 12:16:35 توسط GreyCat)
خوب، اول از همه، من میدانم ممکن است افردای نیز هم اکنون درحال خواندن این مطلب باشند که حتی پرسش را متوجه نمیشوند. اینجا، این کد کار نمیکند:
{ echo oldpass; echo newpass; echo newpass; } | passwd # !این کار نمیکند
اصلاً کاری نمیتوانید بکنید که بتواند در bash عملی بشود. passwd(1) از ورودی استاندارد نمیخواند. این مطلب تعمدی است. برای حراست از شماست. هرگز کلمات عبور برای قرار گرفتن در برنامهها یا تولید شدن توسط برنامهها در نظر گرفته نشدهاند. آنها برای آن در نظر گرفته شدهاند که با انگشتان یک انسان حقیقی، با یک ذکاوت عملیاتی وارد بشوند، و هرگز اصلاً در جایی نوشته نشوند. بنابراین قبل از اینکه ادامه بدهید، احتمالی را که مؤلفین passwd(1) در فکر آن بودند، درنظر بگیرید، و شاید شما نباید سعی در اسکریپت نمودن ورودی passwd(1) بنمایید.
با این وجود، انبوهی پرسش از کاربران به دست ما میرسد که آنها چگونه میتوانند 35 سال امنیت یونیکس را خنثی کنند. و همکاری اشخاصی با راه حلهای مطلوبِ حذف امنیت را با این صفحه مشاهده میکنیم. اگر شما بازهم فکر میکنید آنچه میخواهید این است، ادامه دهید.
اولین رویکرد، ساختن کلمه عبور بهم ریخته خودتان(DES، MD5، Blowfish، یا هر چیزی که سیستم عامل شما استفاده میکند) با استفاده از ابزارهای غیر استانداردی از قبیل http://wooledge.org/~greg/crypt/ یا بسته mkpasswd دبیان/اوبونتو، را شامل میشود. سپس باید آن کلمه عبور درهم ریخته را همراه با فیلدهای اضافی در یک سطر از فایل password-hash سیستم محلی خودتان (شاید /etc/passwd، یا /etc/shadow، یا /etc/master.passwd، یا /etc/security/passwd، یا ... باشد) بنویسید. این کار نیاز به آن دارد که شما صفحات man در سیستم خود را بخوانید، تا دریابید پسورد بهمریخته کجا قرار میگیرد، فایل چه قالببندی نیاز دارد، و سپس کُدی ایجاد کنید که آنرا در آن قالب به تفصیل بنویسد.
یک گونه فرعی آن، مستلزم استفاده از یک ابزار سیستمی خاص برای نوشتن سطرِ کلمه عبور درهم ریخته طراحی شده شما، میباشد. برای مثال، در دبیان/اوبونتو، useradd -m joe -s /bin/bash -p "$(mkpasswd "$password")" به طوری که ما فهمیدهایم، میتواند کارکند.
دومین رویکرد استفاده از expect یا معادل python آن است. من گمان میکنم expect حتی دقیقاً این موضوع را به عنوان یکی از مثالهای رسمیاش دارد.
سرانجام، ابزارهای سیستمی طراحی شده برای این مقصود ممکن است از قبل در سیستم شما موجود باشد. برای مثال، بعضی از سیستمهای گنو-لینوکس دارای فرمان newusers(8) هستند که به طور خاص برای این منظور طراحی شدهاند، یا ابزار chpasswd(8) که میتواند وادار به انجام انواعی از این کارها بشود. یا ممکن است یک پرچم --stdin در فرمان passwd خود داشته باشند. همچنین فرمانهایی از قبیل apropos users یا man -k account را برای دیدن مورد دیگری که شاید وجود داشته باشد امتحان کنید. خلاق باشید.
همچنین پرسش و پاسخ 69 -- میخواهم یک ارتباط ssh (یا scp، یا sftp) را خودکار نمایم، اما نمیدانم چطور کلمه عبور را ارسال کنم را ببینید.
در کنار این مطلب، عکس این پرسش نیز مشکلی است. حداقل تحت لینوکس ، پنهان کردن برنامه به نحوی که ترمینال کنترل کننده وادار شود به طور انتزاعی به هر نوع ورودی و خروجی دلخواه شما متصل بشود، امری بدیهی است. این به معنای آنست که از نطر امنیتی بسیار دشوار است تضمین شود که واقعاً یک کاربر با دسترسی محلی است که به طور مستقیم از صفحه کلید، ورودی را به برنامه شما میدهد. غالباً اشخاص این کار را با خواندن از /dev/tty انجام میدهند. این درست مانند روشی است که برنامه passwd عمل میکند، فقط قدم کوچکی در سست کردن عادات امنیتی نا مناسبی نظیر ذخیره کلمات عبور در فایلهای متن ساده است. کد زیر Bash را اجرا میکند، که برنامهای در FD 3 را میخواند که به طور غیر هوشمندانه ورودیاش را از طریق یک لوله (که به آسانی میتوانست دقیقاً یک فایل باشد)، با استفاده از یک تابع کتابخانه استاندارد پایتون میخواند.
~ $ { echo 'o hi there' | python -c 'import pty; pty.spawn(["bash", "/dev/fd/3"])'; } <<"EOF" 3<&0- <&2 # ممانعت میکند، اثر واقعی ندارد echo از قطع ارتباط ورودی استاندارد <&2 { stty -echo read -p 'Password: ' passw printf '\n%s\n' "password is: $passw" stty echo } </dev/tty EOF o hi there Password: password is: o hi there # نگارش بدون استفاده از پایتون #{ echo 'o hi there' | script -c "bash /dev/fd/3" /dev/null; } <<"EOF" 3<&- 3<&0- <&2 # لینوکس { echo 'o hi there' | script -q /dev/null bash /dev/fd/3; } <<"EOF" 3<&- 3<&0- <&2 # FreeBSD و Mac OS X در { stty -echo read -p 'Password: ' passw printf '\n%s\n' "password is: $passw" stty echo } </dev/tty EOF
علاوه بر این، خواندن از /dev/tty کمی رنجش آور است زیرا طریقهای که کاربران انتظار دارند تغییر مسیرهایشان عمل کنند را نقض میکند. پس این کار را انجام ندهید. بهتر است از [[ -t 0 ]] جهت بازرسی tty و رفتار کردن بر طبق موقعیت استفاده کنید. حتی موقعی که یک مدیر سیستم رفتار معینی را انتظار دارد که بر اساس آن I/O را تغییر میدهد، این مطلب میتواند رنجش آور باشد. اگر شما باید یکی از این ترفندها را به کار ببرید، آن را مستند کنید، و گزینهای برای غیر فعال نمودن رفتار مشروط I/O فراهم نمایید.
پرسش و پاسخ 78 (آخرین ویرایش 2013-03-28 10:54:50 توسط p54A33276)
اگر شما یک کاربر پوسته هستید که میخواهد فعالیتهای خود را ضبط نماید، FAQ #88 را ملاحظه کنید. اگر شما یک مدیر سیستم میباشید که میخواهد بداند، موقعی که یک کاربر تاریخچه پوستهاش را به /dev/null فرستاده یا آن را غیر فعال نموده، چطور میتواند پی ببرد که او چه دستوراتی را اجرا کرده است، چند مشکل برای این مورد وجود دارد....
اولین موضوع این است:
این فرمانِ به ظاهر بی ضرر کاری را انجام میدهد که شما برای آن متصور بودید: پوسته جاری را متوقف میکند. اما، .bash_history فقط موقعی روی دیسک نوشته میشود که به bash اجازه داده شود به طور پاکیزه خارج گردد. به این ترتیب، ارسال سیگنال SIGKILL به bash از ثبت رخداد درفایل .bash_history پیشگیری میکند.
همچنین شاید کاربران متغیرهایی تنظیم کنند که تاریخچه پوسته را غیر فعال نمایند، یا به سادگی .bash_history خودشان را یک پیوند نمادین به /dev/null بسازند. تمام اینها هر کوششی برای جاسوسی آنها از طریق فایل .bash_history آنان را ناکام میکند. سادهترین روش انجام این مورد است
و تاریخچه حتی اگر کاربر به طور پاکیزه از پوسته خارج گردد نیز نوشته نخواهد شد.
دومین موضوع مجوزها هستند. پوسته bash به عنوان یک کاربر اجرا میگردد. این به معنای آنست که کاربر میتواند تمام محتویات تولید شده یا به کار گرفته شده توسط پوسته را بخواند و بنویسد. هر محلی که بخواهید bash در آن ثبت وقایع کند باید قابل نوشتن توسط کاربر اجرا کننده bash باشد. هر چند، این واقعاً به معنی توانایی کاربری که سعی در جاسوسی او میکنید برای پاک کردن اطلاعات از فایل ثبت وقایع میباشد.
سومین موضوع، جایگاه است. فرض کنید شما یک chroot jailزیرنویس1 برای کاربران bash خودتان اتخاذ کنید. این ایده شگفتانگیزی است، و یک قدم مناسب به طرف امنیت سرویسدهنده شما میباشد. هرچند که قرار دادن کاربران در یک زندان chroot به طور معکوس بر توانایی گزارش فعالیت های کاربران نیز تأثیر میگذارد. وقتی که محدودیت ایجاد شد، کاربر شما فقط میتواند در محتوای درون محدوده(jail) ویژه خودش بنویسد. این امر یافتن لاگهای خارجی قابل نوشتن کاربر را آسان میسازد، و مهاجم را قادر میکند نسبت به حالتی که غیر از این باشد، بسیار آسانتر فایلهای ثبت رخداد پنهان شما را پیدا کند.
این امر شما را در چه جایگاهی قرار میدهد؟ متأسفانه، جای خوبی نیست، و مطمئناً آنچه شما میخواستید بدانید نیست. اگر شما میخواهید تمام دستوراتی که توسط کاربر به bash صادر گردیده را ضبط کنید، اولین نیاز آن، ویرایش bash به طوری است که عملاً آن دستورات را در زمان واقعی، موقعی که اجرا میشوند، ضبط کند-- نه وقتی که کاربر قطع ارتباط میکند. دومین ضرورت ثبت کردن آنها به طریقی است که کاربر نتواند باز گردد و آنها را پاک کند(که به معنی آنست که در فایل درج نشوند).
گرچه، هنوز این هم قابل اطمینان نیست، زیرا کاربران نهایی به سادگی میتوانند پوستههای خودشان را انتقال بدهند(upload) و آن را به جای bash هک شده شما اجرا کنند. یا شاید آنها یکی از پوستههای قبلی دیگر بر روی سیستم شما را به جای آن bash استفاده کنند.
Bash 4.1 یک گزینه پیکربندی compile-time(حین ترجمه) برای فعال کردن وقایع نگاری تمام فرمانها از طریق syslog(3) دارد. (توجه کنید که این مطلب فقط در صورتی کمک میکند که کاربران واقعاً آن پوسته را استفاده کنند که در بالا بحث شد.)
برای آنان که به طور مسلم باید بعضی تواناییهای وقایع نگاری را در نگارشهای قدیمیتر bash داشته باشند: میتوانید از وصلهای که در اینجا قرار داده شده استفاده کنید http://wooledge.org/~greg/bash_logging.txt (این patch توسط _sho_ ارائه شده است-- با مسئولیت خودتان استفاده کنید. نتایج بازبینی کُد همراه بهبودبخشیها در اینجا http://phpfi.com/220302 میباشند -- Heiner. متأسفانه به نظر میرسد اکنون آن URL منقضی گردیده است.). توجه نمایید که این وصله از syslog استفاده نمیکند. وصله به عدم اطلاع کاربر از فایل وقایع نگاری تکیه میکند.
جهت یک رویکرد جدیتر برای مشکلِ پیگیری آنچه کاربران شما انجام میدهند، به جای تمرکز روی پوستهها، BSD process accounting2 (مبتنی بر کرنل) را در نظر بگیرید.
پرسش و پاسخ 77 (آخرین ویرایش 2013-01-23 06:08:01 توسط geirha)
این سؤال و تمام پرسشهای مشابه آن با یک AWK یک سطری پاسخ داده میشوند.
awk '{sum += $1} END {print sum}' myfile
یک تقلای کوچک میتواند این خط دستور را با اکثر وظایف مشابه (یافتن میانگین، پرش از سطرهایی با تعداد فیلدهای اشتباه، غیره) وفق بدهد.
برای مثالهای بیشتر کاربرد awk، دستورات یک سطری مفید برای awk را ببینید.
# برای [حالت]یک عدد در هر سطر sum=0; while read -r line; do (( sum += line )); done < "myfile"; echo "$sum"
# جمع اعداد فیلد سوم sum=0; while read -r -a fields; do (( sum += ${fields[2]} )); done < "myfile"; echo "$sum"
# انجام همان کار برای فایلی که در آن سطرها یک سطر نیستند(مترجم: یعنی به یک کاراکتر سطر جدید ختم نمیشوند)، اما باسمیکالن جداشدهاند و فیلدها با کاما جدا شدهاند sum=0; while IFS=, read -rd ';' -a fields; do (( sum += ${fields[2]} )); done < "myfile"; echo "$sum"
توجه نمایید که در مورد فوق لازم است فایل باسمیکالن تمام شود(نه با یک سطر). اگر چنین نیست، میتوانید برای اضافه کردن یک سمیکالن < "myfile" را با <<< "$(<myfile);" تعویض کنید، طوری که read بتواند آخرین سطر را ببیند.
پرسش و پاسخ 76 (آخرین ویرایش 2011-06-25 15:19:06 توسط GreyCat)