یک روش زمخت(بلکه غیر سریع)این است:
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"
پرسش و پاسخ 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 را ملاحظه نمایید.
پرسش و پاسخ 10 (آخرین ویرایش 2011-05-27 12:22:09 توسط GreyCat)
اکثر فرمانهای استاندارد یونیکس وقتی به طور غیر محاورهای به کارمیروند، خروجی را در حافظه میانجی قرار میدهند. بدین معنی که بلافاصله، هر کاراکتر(یا حتی هر سطر) را نمینویسند، بلکه به جای آن، قبل از آنکه ابداً چیزی چاپ نمایند، تعداد زیادی از کاراکترها (اغلب 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 خودتان است یا به کُد منبع آن دسترسی دارید، میتوانید به این شکل میانگیری را غیرفعال کنید
setvbuf(stdout, 0, _IONBF, 0);
بسته expect یک برنامه unbuffer دارد که به طور مؤثری شگردی به برنامههای دیگر میزند که همیشه رفتاری داشته باشند چنانکه گویی به صورت محاورهای به کار رفتهاند(و اغلب میانگیری غیر فعال میشود). این هم یک مثال ساده:
tail -f logfile | unbuffer grep 'foo bar' | awk ...
برنامه expect و unbuffer ابزارهای استاندارد POSIX نیستند، اما شاید از قبل در سیستم شما نصب شده باشند.
نگارشهای اخیر coreutils گنو (از 7.5 به این طرف)با یک برنامه سودمند دلپسند به نام stdbuf همراه میباشند، که میتواند در میان سایرین برای "unbuffer" نمودن خروجی استاندارد یک فرمان به کار برود. در اینجا کاربرد اساسی آن برای مثال ما آمده است:
tail -f logfile | stdbuf -oL grep 'foo bar' | awk ...
در کُد فوق، "-oL" خروجی استاندارد را به طور سطری میانگیری میکند، حتی میتوانید از "-o0" برای غیرفعال کردن کامل آن استفاده کنید. صفحات man و info دارای تمام جزئیات میباشند.
stdbuf ابزار استاندارد POSIX نیست، اماشاید از قبل در سیستم شما نصب شده باشد(اگر از توزیعهای اخیر لینوکس استفاده میکنید،احتمالاً موجود خواهد بود).
اگر میخواهید به جای فیلتر کردن سطرهای انطباق نیافته، به سادگی عبارت مورد جستجو را متمایز(highlight) نمایید،میتوانید در عوض فیلتر tail -f از برنامه less استفاده کنید:
$ less program.log
داخل less، با دستور '/' یک جستجو را شروع کنید(مشابه جستجو در vi). یا برنامه less را با گزینه الگو -p شروع کنید.
حالا less را در وضعیت "follow" قرار بدهید، که به طور پیشفرض با shift+f به این وضعیت میرود.
وضعیت "follow" با یک وقفه که احتمالاً در سیستم شما control+c است، خاتمه مییابد. دستور '/' عبارتهای منظم را قبول میکند، بنابراین شما میتوانستید کارهایی مانند متمایز کردن تمام سطری که عبارت جستجو در آن ظاهر میشود را انجام دهید. برای جزئیات، man less را کاوش نمایید.
اگر شما از پوسته ksh یا Bash نگارش 4.0+ استفاده میکنید، برای هر کاری که شما حقیقتاً تلاش میکنید با tail -f انجام بدهید، کاربرد coproc و fflush() جهت ایجاد پردازش کمکی میتواند سودمند باشد. خوب توجه نمایید که coproc خودش میانگیری را بررسی نمیکند( در واقع مستعد مسائل میانگیری است -- از اینرو به fflush ارجاع میدهد). در اینجا فقط به coproc اشاره شد، به علت آنکه وقتی شخصی به طور پیوسته سعی در کنترل و عکسالعمل نشان دادن به رشد آهسته فایل(یا لوله) مینماید، شاید بخواهد کاری کند که از کمک پردازشها سود خواهد برد.
پرسش و پاسخ 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 ، یا سطرهای جدید باشد، میتواند پردازش شود.
پرسش و پاسخ 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 آرایه را برمیگرداند.
پرسش و پاسخ7 (آخرین ویرایش 2011-05-26 13:28:27 توسط sbl-eh4-rp1)