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

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

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

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

پرسش و پاسخ شماره ۱۱



چگونه می‌توانم سطر شماره n از یک فایل را چاپ کنم؟

یک روش زمخت(بلکه غیر سریع)این است:

    sed -n ${n}p "$file"

اما حتی اگر فقط سطر سوم را خواسته باشید، این تمام فایل را می‌خواند، که برای اجتناب از آن می‌توان، جهت چاپ سطر ‎ $n‎ ازفرمان "p" استفاده کنیم و به دنبال آن "q" را برای خروج از اسکریپت به کار ببریم:

    sed -n "$n{p;q;}" "$file"

یک شیوه دیگر، ربودن آخرین سطر از یک لیست n سطری است:

   head -n $n $file | tail -n 1

راهکار دیگر، به کار بردن AWK است:

   awk "NR==$n{print;exit}" file

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

   x=3 y=4
   sed -n "$x,${y}p;${y}q;" "$file"
                             #   چاپ می‌کند و خارج می‌شود $y تا$x سطرهای 
   head -n $y "$file" | tail -n $((y - x + 1))             #   همانطور‎
   head -n $y "$file" | tail -n +$x     # پشتیبانی کند tail اگر برنامه 
   awk "NR>=$x{print} NR==$y{exit}" "$file"            #  به همان طریق

در Bash نگارش 4، با استفاده از دستور داخلی mapfile یک راهکار فشرده مختص-bashمی‌تواند حاصل شود. با همراه کردن شناسه با گزینه ‎ -n ‎ این فرمان، بیش از یک سطر می‌تواند در آرایه MAPFILE خوانده شود:

   mapfile -ts $((n-1)) -n 1 < "$file"
   echo "${MAPFILE[0]}"

همچنین mapfile می‌تواند به طور مشابهی با head به کار برود، در صورتیکه از مسائل میانگیری لوله بودن ورودی اجتناب می‌شود:

   { mapfile -n $n; head -n 1; } <"$file"

See Also


CategoryShell

پرسش و پاسخ 11 (آخرین ویرایش ‎ 2012-02-27 02:27:07 ‎ توسط ormaaj)


پرسش و پاسخ شماره ۱۰



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

با برنامه cpio :

 cd "$srcdir" &&
 find . -type d -print | cpio -pdumv "$dstdir"

یا با برنامه pax:

 cd "$srcdir" &&
 find . -type d -print | pax -rwdv  "$dstdir"

یا با globbing خاص پوسته zsh:

 zsh -ec '
 cd -- "$srcdir"
 dirs=(**/*(ND))
 cd -- "$dstdir"
 mkdir -p -- $dirs'

یا با tar گنو، وترکیب دستوری بلندتر:

 cd "$srcdir" &&
 find . -type d -print | tar c --files-from - --no-recursion |
   tar x --directory "$dstdir"

این دستور با find لیستی از نام دایرکتوریها ایجاد می‌کند، به طور غیر بازگشتی فقط نام دایرکتوریها را به یک آرشیو اضافه می‌کند، و آنرا از طریق لوله به دومین tar می‌دهد که در محل مقصد آن را استخراج کند. به طوری که می‌توانید ببینید، tar کمترین مناسبت را برای این منظور دارد، اما اشخاص فقط شیفته آن هستند، بنابراین فقط برای آرام کردن جمعیت علاقمندان tar در اینجا گنجانده شده. (توجه: حتی شما به هیچوجه نمی‌توانید این را با یک tar معمولی یونیکس انجام بدهید. همچنین توجه کنید: چیزی به عنوان "standard tar" وجود ندارد، در نتیجه هردو برنامه tar و cpio به لطف pax به طور عمدی در POSIX از قلم افتاده‌اند.)

اگر نام دایرکتوریها شامل کاراکترهای سطرجدید باشد راه حل zsh فوق تقریبا شکست می‌خورد. دست کم در بسیاری سیستم‌های مدرن BSD/GNU، آنها برای از عهده برآمدن، می‌توانند با استفاده از ‎ find -print0‎ و یکی از موارد ‎ pax -0‎ یا ‎cpio -0‎ یا ‎tar --null‎ به طور جزئی اصلاح شوند(مستندات سیستم خود را برای دیدن آنکه کدام یک از این برنامه‌ها را دارید، و کدام ملحقات در دسترس می‌باشند، بررسی نمایید).

اگر می‌خواهید به جای فایلهای کامل، فایلهای بدون محتوا(خالی) با ‎find(1)‎ گنو ایجاد کنید، مورد ذیل احتمالاً ساده‌ترین روش است.فرمان find فایلهای معمولی رابه طور مصنوعی(فایلهای خالی با همان نشانه‌های زمان) بازتولید می‌کند:

 cd "$srcdir" &&
 # :اول با یکی از فرمانهای فوق دایرکتوریها را ایجاد نمایید، سپس
 find . -type f -exec touch -r {} "$destination"/{} \;

آگاه باشید،به هرحال، مطابق POSIX، وقتی ‎{}‎ به عنوان یک شناسه حضور ندارد، رفتار find مشخص نشده است. به این دلیل، راه حل پایین قابل حمل‌تر(و احتمالاً سریعتر) از مورد قبلی است:

 dstdir=whatever; export dstdir
 find . -type f -exec sh -c 'for i; do touch -r "$i" "$dstdir"/"$i"; done' _ {} +

اگر برنامه find شما نمی‌تواند ‎ -exec +‎ را مدیریت نماید،آنوقت می‌توانید از ‎ \;‎ به جای + در انتهای فرمان استفاده کنید. برای توضیحات UsingFind را ملاحظه نمایید.


CategoryShell

پرسش و پاسخ 10 (آخرین ویرایش‎ 2011-05-27 12:22:09 ‎ توسط GreyCat)


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



میانگیری(buffering) چیست؟ یا، چرا این سطر فرمان من خروجی ندارد:

tail -f  logfile | grep 'foo bar' | awk ...‎

اکثر فرمانهای استاندارد یونیکس وقتی به طور غیر محاوره‌ای به کارمی‌روند، خروجی را در حافظه میانجی قرار می‌دهند. بدین معنی که بلافاصله، هر کاراکتر(یا حتی هر سطر) را نمی‌نویسند، بلکه به جای آن، قبل از آنکه ابداً چیزی چاپ نمایند، تعداد زیادی از کاراکترها (اغلب 4 کیلو بایت) را جمع‌آوری می‌کنند. در حالت فوق، فرمان grep خروجی‌اش را میانگیری می‌کند، و بنابراین awk ورودی‌اش را فقط در قطعات بزرگ دریافت می‌کند. میانگیری به طور عمده‌ای کارایی عملیات ورودی و خروجی را افزایش می‌دهد, و معمولاً به طریقی انجام می‌شود که تأثیر قابل رؤیت برای کاربر ندارد. دستور ساده ‎ tail -f‎ از یک نشست ترمینال محاوره‌ای به خوبی کار می‌کند، اما موقعی که دستور بخشی از یک خط لوله پیچیده باشد، شاید فرمان نتواند زمان واقعی(نزدیک) نیاز به خروجی نهایی را تشخیص بدهد. خوشبختانه، چند شگرد برای کنترل رفتار میانگیری ورودی-خروجی در دسترس می‌باشد.

مهمترین مطلب برای درک میانگیری آن است که نویسنده آنرا انجام می‌دهد، نه خواننده.

محو کردن دستورات غیر ضروری

در این پرسش، ما لوله ‎tail -f logfile | grep 'foo bar' | awk ...‎ را داریم (با فرمان واقعی مشخص نشده AWK). اگر ما حقیقتاً ‎ tail -f logfile‎ را اجرا نماییم، مشکلی وجود ندارد، به دلیل آنکه ‎ tail -f‎ هرگز خروجی‌اش را بافر نمی‌کند. اگر ‎ tail -f logfile | grep 'foo bar'‎ را به طور محاوره‌ای اجرا کنیم نیز مشکلی وجود ندارد، زیرا grep نیز اگر خروجی استانداردش ترمینال باشد، خروجی‌اش را میانگیری نمی‌کند. گرچه، اگر خروجی grep به درون برنامه دیگری(ازقبیل یک فرمان AWK) لوله کشی بشود، برای بهبود کارایی، میانگیری را شروع می‌کند.

در این مثال عملی، برنامه grep واقعاً زائد است. ما می‌توانیم آنرا حذف کنیم، و AWK را برای انجام عمل فیلتر کردن و هر چیز دیگری که انجام می‌دهد داشته باشیم:

tail -f logfile | awk '/foo bar/ ...'

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

فرمان شما ممکن است از قبل بافر نکردن خروجی را پشتیبانی نماید

برخی برنامه‌هابه طور ویژه‌ای گزینه‌های خاص خط فرمان را برای این قبیل مشکلات فراهم می‌کنند:

grep (به طور نمونه،گنو نگارش 2.5.1)

--line-buffered

sed (به عنوان مثال، گنو نگارش 4.0.6)

-u,--unbuffered

awk (برخی نگارشهای گنو)

-W interactive, or use the fflush() function

tcpdump و tethereal

-l

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

از کار انداختن میانگیری در برنامه کاربردی C

اگر این متعلق به یک برنامه کاربردی C خودتان است یا به کُد منبع آن دسترسی دارید، می‌توانید به این شکل میانگیری را غیرفعال کنید

setvbuf(stdout, 0, _IONBF, 0);

unbuffer

بسته expect یک برنامه unbuffer دارد که به طور مؤثری شگردی به برنامه‌های دیگر می‌زند که همیشه رفتاری داشته باشند چنانکه گویی به صورت محاوره‌ای به کار رفته‌اند(و اغلب میانگیری غیر فعال می‌شود). این هم یک مثال ساده:

tail -f logfile | unbuffer grep 'foo bar' | awk ...

برنامه expect و unbuffer ابزارهای استاندارد POSIX نیستند، اما شاید از قبل در سیستم شما نصب شده باشند.

stdbuf

نگارشهای اخیر coreutils گنو (از 7.5 به این طرف)با یک برنامه سودمند دلپسند به نام stdbuf همراه می‌باشند، که می‌تواند در میان سایرین برای "unbuffer" نمودن خروجی استاندارد یک فرمان به کار برود. در اینجا کاربرد اساسی آن برای مثال ما آمده است:

tail -f logfile | stdbuf -oL grep 'foo bar' | awk ...

در کُد فوق، ‎"-oL"‎ خروجی استاندارد را به طور سطری میانگیری می‌کند، حتی می‌توانید از ‎"-o0"‎ برای غیرفعال کردن کامل آن استفاده کنید. صفحات man و info دارای تمام جزئیات می‌باشند.

stdbuf ابزار استاندارد POSIX نیست، اماشاید از قبل در سیستم شما نصب شده باشد(اگر از توزیع‌های اخیر لینوکس استفاده می‌کنید،احتمالاً موجود خواهد بود).

less

اگر می‌خواهید به جای فیلتر کردن سطرهای انطباق نیافته، به سادگی عبارت مورد جستجو را متمایز(highlight) نمایید،می‌توانید در عوض فیلتر‎ tail -f‎ از برنامه less استفاده کنید:

$ less program.log
  • داخل less، با دستور '/' یک جستجو را شروع کنید(مشابه جستجو در vi). یا برنامه less را با گزینه ‎الگو ‎-p‎ شروع کنید.

  • این کار هر نمونه از عبارت جستجو را متمایز می‌کند.
  • حالا less را در وضعیت "follow" قرار بدهید، که به طور پیش‌فرض با ‎shift+f‎ به این وضعیت می‌رود.

  • قسمت انتهایی فایل تعیین شده را به طور فیلتر نشده، با متمایز شدن عبارت جستجو، دریافت خواهید نمود.

وضعیت "follow" با یک وقفه که احتمالاً در سیستم شما ‎ control+c‎ است، خاتمه می‌یابد. دستور '/' عبارت‌های منظم را قبول می‌کند، بنابراین شما می‌توانستید کارهایی مانند متمایز کردن تمام سطری که عبارت جستجو در آن ظاهر می‌شود را انجام دهید. برای جزئیات، ‎ man less‎ را کاوش نمایید.

coproc

اگر شما از پوسته ksh یا Bash نگارش 4.0+ استفاده می‌کنید، برای هر کاری که شما حقیقتاً تلاش می‌کنید با ‎ tail -f‎ انجام بدهید، کاربرد coproc و ‎fflush()‎ جهت ایجاد پردازش کمکی می‌تواند سودمند باشد. خوب توجه نمایید که coproc خودش میانگیری را بررسی نمی‌کند( در واقع مستعد مسائل میانگیری است -- از اینرو به fflush ارجاع می‌دهد). در اینجا فقط به coproc اشاره شد، به علت آنکه وقتی شخصی به طور پیوسته سعی در کنترل و عکس‌العمل نشان دادن به رشد آهسته فایل(یا لوله) می‌نماید، شاید بخواهد کاری کند که از کمک پردازشها سود خواهد برد.

مطالعه بیشتر


CategoryShell

پرسش و پاسخ 9 (آخرین ویرایش‎ 2012-09-14 07:43:13 ‎ توسط dslb-088-072-055-065)


پرسش و پاسخ شماره ۸



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

90% اوقات، تمام آنچه لازم دارید یکی از اینهاست:

# :سطرهای انطباق یافته را چاپ می‌کند (GNU grep)
grep -r -- "$search" .

# :فقط نام فایلها را برگشت و چاپ می‌کند (GNU grep)
grep -r -l -- "$search" .

اگر برنامه grep شما فاقد گزینه ‎ -r ‎ است، می‌توانید از find استفاده کنید، یا اگرمی‌خواهید از برخورد با پیوندهای نمادین اجتناب نمایید:

find . -type f -exec grep -l -- "$search" {} \;

کاراکترهای {} با نام فایل تعویض خواهند شد.

این فرمان کندتر از آنست که لازم بشود، زیرا find فقط با یک نام فایل grep را فراخوانی می‌کند، که منجر به فراخوانی‌های بسیار grep (یکبار برای هر فایل) می‌گردد. چون grep نام فایلهای چندگانه را در خط فرمان می‌پذیرد، به find می‌تواند دستور داده شود که یکمرتبه آنرا با چندین نام فایل فراخوانی کند:

find . -type f -exec grep -l -- "$search" {} +

کاراکتر '+' در انتها به find دستور می‌دهد grep را با هر تعداد نام فایل ممکن فراخوانی کند، صرفه جویی پردازش و اجرای سریعتر. این مثال در find پوسته POSIX کار می‌کند، به عنوان مثال در سولاریس به همان خوبی findخیلی اخیر گنو.

یونیکس سنتی یک برنامه کمکی به نام xargs برای همین منظور دارد:

find . -type f | xargs grep -l -- "$search"

به هرحال، اگر نام فایل شامل فاصله یا سایر فوق کاراکترها باشد، نیاز به استفاده از گزینه ‎-print0‎ گنو یا BSD دارید:

find . -type f -print0 | xargs -0 grep -l -- "$search"

گزینه‌های ‎ -print0‎ و ‎-0‎ اطمینان ایجاد می‌کنند که هر نام‌ فایل حتی آنکه شامل فاصله‌ها، کاراکترهای TAB ، یا سطرهای جدید ‌باشد، می‌تواند پردازش شود.


CategoryShell

پرسش و پاسخ 8 (آخرین ویرایش ‎ 2011-05-26 13:31:47 ‎ توسط sbl-eh4-rp1)


پرسش و پاسخ شماره ۷



آیا تابعی وجود دارد که طول یک رشته را بازگرداند؟

سریع‌ترین روش، نیازی به برنامه‌های خارجی ندارد(اما در پوسته بورن قابل استفاده نمی‌باشد)

# POSIX  در پوسته
${#varname}

یا برای پوسته Bourne:

# Bourne
expr "$varname" : '.*'

( برنامه expr تعداد کاراکترهای انطباق یافته با الگوی‎ .*‎ را چاپ می‌کند، که همان طول رشته است.)

یا:

# Bourne, with GNU expr(1)
expr length "$varname"

(فقط expr BSD و گنو)

این روش دوم در POSIX تعریف نشده است، بنابراین قابل حمل به همه سکوها نیست. در هرحال، اگر ‎$varname‎ به "length" بسط ‌یابد، روش اول با expr گنو و BSD ناموفق است.

یک روش قابل حمل این است:

expr \( "X$varname" : ".*" \) - 1

ممکن است کسی از awk استفاده کند:

# Bourne
awk -v x="$varname" 'BEGIN {print length(x)}'

باوجود اینکه آن یکی به ازای مقادیری از ‎$varname‎ که شامل کاراکترهای \ باشند ناموفق خواهد شد، بنابراین شاید این را ترجیح بدهید:

# Bourne with POSIX awk
awk  'BEGIN {print length(ARGV[1]);exit}' "$varname"


نیازهای مشابه:

# Korn/Bash
${#arrayname[@]}

تعداد عناصر یک آرایه را باز می‌گرداند.

# Korn/Bash
${#arrayname[i]}

طول عضو i آرایه را برمی‌گرداند.


CategoryShell

پرسش و پاسخ7 (آخرین ویرایش ‎ 2011-05-26 13:28:27‎ توسط sbl-eh4-rp1)