نمیتوانید آنرا به تنهایی با تغییر مسیر bash انجام دهید، برعکسِ>> وجود ندارد....
برای درج محتویات در ابتدای فایل، میتوانید از یک ویرایشگر استفاده کنید، برای مثال ex:
ex file << EOF 0a header line 1 header line 2 . w EOF
یا ed:
printf '%s\n' 0a "line 1" "line 2" . w | ed -s file
ex همچنین در صورت فقدان کاراکتر سطرجدید در انتهای فایل آن را به فایل اضافه میکند.
یا با استفاده از مواردی از این قبیل میتوانید فایل را بازنویسی کنید:
{ echo line; cat file ;} >tmpfile && mv tmpfile file echo line | cat - file > tmpfile && mv tmpfile file
برخی اشخاص اصرار دارند از چکش sed برای کوبیدن تمام پیچها استفاده کنند:
sed "1iTEXTTOPREPEND" filename > tmp && mv tmp filename
راه حلهای بسیار دیگری نیز وجود دارد.
پرسش و پاسخ 90 (آخرین ویرایش 2011-03-15 17:02:32 توسط GreyCat)
خواندن سطر به سطر فایل، اگر یک دستور در داخل حلقه نیز stdin را بخواند، میتواند فایل ورودی را تهی کند، برای مثال:
# مثالی که کار نمیکند while IFS= read -r file; do ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv done < <(find . -name '*.avi')
# مثالی که کار نمیکند while read host; do ssh "$host" some command done <hostslist
در اینجا چه اتفاقی رخ میدهد؟ اجازه بدهید مثال اول راببینیم. read سطری از ورودی استاندارد(FD 0) میخواند،آن را در پارامتر file قرار میدهد، و بعد ffmpeg اجرا میشود. مانند هر برنامه دیگری که شما از BASH اجرا میکنید، ffmpeg نیز ورودی استاندارد را به ارث میبرد، که بنا به دلایلی از آن میخواند. من نمیدانم چرا. اما در هر وضعیتی، موقعی که ffmpeg از stdin میخواند، تمام ورودی را از فرمان find جذب میکند، حلقه از گرسنگی میمیرد.
این هم چگونه کارآمد ساختن آن:
while IFS= read -r file; do ffmpeg -i "$file" -vcodec libxvid -acodec libfaac -ar 32000 "${file%.avi}".mkv </dev/null done < <(find . -name '*.avi')
به تغییر مسیر در سطر ffmpeg توجه نمایید: </dev/null. مثال ssh میتواند به همین ترتیب ، یا با گزینه -n اصلاح بشود (حداقل با OpenSSH).
گاهی اوقات شاید کار کردن با آنچه از ورودی استاندارد خوانده میشود، با حلقههای بزرگ دشوار گردد، یا ممکن است برنامه موقعی که شما </dev/null را به آن اضافه میکنید رفتارش را تغییر بدهد. در این وضعیت شما میتوانید کاری کنید که فرمان read از توصیفگر فایل متفاوتی استفاده کند، که کمتر محتمل است یک برنامه تصادفی از آن بخواند:
while read <&3 line; do ...... done 3<file
یا از گزینه -u فرمان read استفاده کنید(POSIX نیست):
# Bash while read -u 3 line; do ...... done 3<file
پرسش و پاسخ 89 (آخرین ویرایش 2013-05-17 20:58:31 توسط GreyCat)
این روش برای آن طراحی شده تا به شما امکان بدهد ثبت وقایع کامل دوستانهای از تمام فرمانهای اجرا شده توسط کاربر را ذخیره کنید، مقصود از آن حسابرسی امن فرمانها نمیباشد -صفحه امنیت bash در برابر پاکسازی تاریخچه را ببینید.
به طور پیشفرض، Bash تاریچهاش رافقط موقع خروج به روزرسانی میکند، و تاریخچه موجود را با یک نگارش جدیدتر بازنویسی میکند. این مطلب به دو دلیل شما را از داشتن یک فایل ثبت وقایع تاریخچه محروم میکند:
برای حل مشکل نخست، ما گزینه histappend پوسته را تنظیم میکنیم که باعث میشود تمام سطرهای تاریخچه جدید در فایل پیوست بشوند، و تضمین میشود که تاریخچه ورود(login)های متعدد یکدیگر را رونویسی نمیکنند.
برای ممانعت از مفقود شدن سطرهای تاریخچه در وضعیتی که Bash به طور غیرعادی خارج شود، لازم است مطمئن شویم که سطرها بعد از هر دستور نوشته میشوند. می توانیم از دستور داخلی پوسته به شکل history -a استفاده کنیم که باعث نوشته شدن فوری تمام سطرهای تاریخچه میگردد، و ما میتوانیم با افزودن آن به متغیر PROMPT_COMMAND اجرای آن را خودکار نماییم. این متغیر محتوی فرمانی برای اجرا شدن قبل از نمایش هر اعلان فرمان جدید است، و بنابراین بعد از هر فرمان پوسته محاورهای، اجرا میشود.
توجه نمایید که اجرای 'history -a' بعد از هر فرمان تأثیر دوجانبه دارد:
برای انجام تمام این موارد، کد زیر را در فایل ~/.bashrc خودتان به کار ببرید:
HISTFILESIZE=400000000 HISTSIZE=10000 PROMPT_COMMAND="history -a" export HISTSIZE PROMPT_COMMAND shopt -s histappend
در کد فوق همچنین حداکثر تعداد سطرهای تاریخچه را که در حافظه ذخیره میشوند، افزایش دادهایم و هر محدودیتی برای خود فایل تاریخچه را حذف نمودیم. پیشفرض اینها 500 سطر است، که اگر شما کاربر فعالی هستید باعث میشود به سرعت شروع کنید به از دست دادن سطر فرمانهای تاریخچه. با تنظیم کردن HISTFILESIZE به یک کمیت درشت، یک فایل به اندازه کافی بزرگ را به طوری که عملاً نامحدود است تأمین میکنیم - و با تنظیم $HISTSIZE، تعداد این سطرها که در حافظه نگهداری میشوند را محدود نمودهایم. متأسفانه، bash کل فایل تاریخچه را قبل از آنکه کوتاه شده آن به اندازه $HISTSIZE را به حافظه کپی کند، میخواند - بنابراین اگر فایل تاریخچه فرمان شما خیلی بزرگ شود، زمان شروع اولیه bash شما میتواند به طور رنجشآوری بالا برود. حتی بدتر، بارگیری فایل تاریخچه بزرگ و بعد کوتاهسازی آن به اندازه $HISTSIZE منجر به نفخ منابع مورد استفاده میشود، bash حافظه بسیار بیشتری از وقتی که فایل تاریخچه فقط به اندازه $HISTSIZE سطر باشد، مصرف میکند. بنابراین اگر انتظار دارید فایل تاریخچه شما خیلی بزرگ شود، برای مثال بالای 20,000 سطر، باید به طور متناوب آنرا بایگانی کنید. بایگانی فایلهای تاریخچه در پایین را ببینید.
PROMPT_COMMAND ممکن است قبلاً در تنظیمات شما به کار رفته باشد، برای مثال شامل کدهای کنترل برای به روزرسانی یک نوار نمایش XTerm با اعلان فرمان شما. اگر مال شما از قبل در استفاده است میتوانید به این طریق به آن پیوست کنید: PROMPT_COMMAND="${PROMPT_COMMAND:-:} ; history -a"
همچنین شاید بخواهید متغیرهای HISTIGNORE و HISTCONTROL را برای کنترل آنچه ذخیره میشود، برای مثال حذف سطرهای تکراری، به کار ببرید - به هر حال انجام آن شما را از دیدن آن که چندبار یک فرمان معین توسط کاربری اجرا شده باز میدارد، و به طور دقیق وقتی( HISTTIMEFORMAT نیز برقرار شده باشد).
سرانجام، توجه کنید که به علت اجرای PROMPT_COMMAND درست قبل از اینکه اعلان فرمان چاپ شود، ممکن است شما آخرین سطر فرمان را در صورتی که پوسته در اثنای انجام این فرمان فسخ بشود، از دست بدهید. به عنوان یک مثال، ملاحظه کنید: this_cmd_is_never_written_to_history ; kill -9 $$
نتیجه عمل فوق، یک فایل تاریخچه با مقدار بسیاری فرمانهای تکراری است. پیوست نمودن تاریخچه باعث میشود فایل تاریخچه شما به وسیله همه تاریخچههای بارگذاری شده پوسته در هر نوبت، رشد کند.
به طور مهمتر، مطلب عمدهای که با حساسیت نسبت به تاریخچه برای ما اهمیت دارد آن است که قادر به یافتن دستوراتی باشیم که قبلاً اجرا شدهاند. اسکریپت زیر تمام دستوراتی که از قبل در فایل تاریخچه هستند را از آن حذف میکند، در حالیکه ترتیب دستورات دست نخورده حفظ میگردد، به طریقی که آخرین دستورات اجرا شده در انتهای فایل باقی میمانند (یعنی حفظ آخرین وقوع یک فرمان، نه اولین مورد).
awk 'NR==FNR && !/^#/{lines[$0]=FNR;next} lines[$0]==FNR' "$HISTFILE" "$HISTFILE" > "$HISTFILE.compressed" && mv "$HISTFILE.compressed" "$HISTFILE"
پس از چند ماه، این اسکریپت فایل تاریخچهام را از 761474 به 2349 سطر فشرده نمود.
یکبار که شما این روشها را فعال کنید، متوجه خواهید شد که تاریخچه bash خیلی بیشتر ارزشمند میشود، میسر نمودن فراخوانی هر فرمانی که هر زمانی اجرا کردهاید. همینطور، شما باید مطمئن شوید که فایل(های) تاریخچهتان به پشتیبانهای مقرر شما ضمیمه میگردد.
همچنین ممکن است برای ممانعت از بارگیری تاریخچه کامل در حافظه، توسط هر پوسته bash جدید، بخواهید بایگانی منظم فایل تاریخچه خود را فعال نمایید. با یک فایل تاریخچه شامل 10,000 مدخل، bash در Solaris 10 تقریباً از 5.5MB حافظه استفاده میکند، با تأخیر غیرقابل ارزیابی در شروع اولیه( من فرض کنم با $HOME روی یک دیسک محلی؟ -- GreyCat). با تاریخچهای به اندازه 100,000 مدخل این به 10MB با تأخیر قابل ملاحظه 3-5 ثانیه در زمان شروع میرسد. بایگانی متناوب برای پاک کردن قدیمیترین سطرهای گزارش و برای اجتناب از هرز دادن منابع قابل توصیه است، مخصوصاً اگر RAM بسیار ارزشمند باشد. (بزرگترین ~/.bash_history من بعد از 1.5 ماه 7500 مدخل است.)
این امر به بهترین وجه از طریق ابزاری که میتواند بخشی از فایل را بایگانی کند انجام شده است. یک اسکریپت ساده برای انجام این کار اینطور خواهد بود:
#!/bin/bash umask 077 max_lines=10000 linecount=$(wc -l < ~/.bash_history) if (($linecount > $max_lines)); then prune_lines=$(($linecount - $max_lines)) head -$prune_lines ~/.bash_history >> ~/.bash_history.archive \ && sed -e "1,${prune_lines}d" ~/.bash_history > ~/.bash_history.tmp$$ \ && mv ~/.bash_history.tmp$$ ~/.bash_history fi
این اسکریپت سطرهای کافی از بالای فایل تاریخچه را برای کوتاه کردن آن به اندازه X سطر پاک میکند، باقیمانده را در فایل ~/.bash_history.archive درج میکند. این از توانایی هرسکردن HISTFILESIZE تقلید میکند، اما باقیمانده را به جای حذف کردن، بایگانی میکند - تضمین میکند شما همواره میتوانید تاریخچه گذشته را با grep ~/.bash_history* جستجو نمایید.
چنین اسکریپتی میتواند به طور شبانه یا هفتگی از crontab شخصی شما برای فعال کردن تهیه بایگانی دورهای به کار برود. توجه نمایید که چندین کاربر را مدیریت نمیکند و فقط تاریخچه کاربر جاری را بایگانی خواهد نمود - توسعه دادن آن جهت اجرا برای تمام کاربران(همچون root) به عنوان تمرین برای خواننده باقی گذاشته شد.
پرسش و پاسخ 88 (آخرین ویرایش 2013-07-21 20:03:09 توسط ppp091138132034)
تعدادی روش اختیاری وجود دارد، اکثر آنها اختصاصی سیستم هستند. همچنین دقیقاً بستگی به آن دارند که چرا اطلاعات را میخواهید، در اکثر حالتها، روشهای دیگری برای انجام هدف واقعی شما وجود دارد. شما نیاز ندارید خروجی ls را تفکیک نمایید در صورتیکه برای اجتناب از انجام آن، یک روش امکان پذیر موجود باشد.
بسیاری از موقعیتهایی که در آنها شاید شما جویای مجوزها بشوید -- از قبیل من میخواهم هر فایلی که بیت setuid زیرنویس 1 آن تنظیم شده را پیدا کنم -- میتوانند با فرمان find(1) مدیریت بشوند.
برای برخی پرسشها، از قبیل میخواهم مطمئن شوم این فایل مجوز 0644 دارد، حقیقتاً شما نیاز به بررسی آن که مجوزها کدامند، ندارید. شما میتوانید فقط از chmod 0644 myfile استفاده کنید و آنها را به طور مستقیم تنظیم کنید. و اگر واقعا به جای مجبور کردن آنها به مجوز مورد نظر، نیاز به بررسی مجوزها دارید، آنوقت میتوانید از گزینه -perm فرمان find استفاده کنید.
اگر میخواهید ببینید آیا میتوانید فایلی را بخوانید، بنویسید، یا اجرا کنید، test -r , -x , -w موجود میباشند.
اگر میخواهید ببینید آیا اندازه یک فایل صفر است یا خیر، نیازی به خواندن اندازه فایل در یک متغیر ندارید. به جای آن میتوانید فقط test -s را به کار ببرید.
اگر میخواهید زمان ویرایش یک فایل را به دیگری کپی کنید، میتوانید touch -r را به کار ببرید. فرمان chown در بعضی سیستمهای گنو-لینوکس دارای گزینه --reference میباشد که به همان طریق کار میکند، به شما اجازه میدهد مالک، و گروه را از یک فایل به دیگری کپی نمایید.
اگر نیازهای شما مطابق با یکی از آنها نیست، و واقعاً احساس میکنید باید فوق دادههای یک فایل را استخراج و در یک متغیر قرار بدهید، آنوقت ما میتوانیم چند جایگزین را در نظر بگیریم:
در سیستمهای گنو-لینوکس، *BSD و احتمالاً دیگران، فرمانی به نام stat(1) وجود دارد. در سیستمهای قدیمیتر گنو-لینوکس، این فرمان گزینهای نمیگیرد -- فقط نام فایل -- و شما باید خروجی آن را تفکیک نمایید.
$ stat / File: "/" Size: 1024 Filetype: Directory Mode: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Device: 8,0 Inode: 2 Links: 25 Access: Wed Oct 17 14:58:02 2007(00000.00:00:01) Modify: Wed Feb 28 15:42:14 2007(00230.22:15:49) Change: Wed Feb 28 15:42:14 2007(00230.22:15:49)
در این وضعیت، شخص میتوانست 0755 را از سطر Mode:، با استفاده از awk یا فرمانهای مشابهی استخراج کند.
در سیستمهای گنو-لینوکس جدیدتر:
$ stat -c %a / 755
به طور آشکاری برای تجزیه خیلی آسانتر است. ترکیب دستوری با BSDها (NetBSD و OpenBSD و FreeBSD و مشتقات آنها مانند Apple OS/X)، متفاوت است و لازم است شما مجوزها را از روی mode استخراج کنید:
mode=$(stat -f %p -- "$filename") perm=$(printf %o "$((mode & 07777))")
perl -e 'printf "%o\n", 07777 & (stat $ARGV[0])[2]' "$filename"
این کد همان رشته اُکتال مثال stat -c %a را باز میگرداند، اما خیلی بیشتر قابل حمل است. (و آهستهتر).
فرمان find گنو دارای یک گزینه -printf است که میتواند هر فوقدادهای از یک فایل را به خروجی ارسال کند:
find "$filename" -prune -printf '%m\n'فرمان find بیش از یک دهه قدیمیتر از stat گنو است و همچنین میتواند فوقداده چندین فایل در یک دایرکتوری را ارائه کند. به هر حال مراقب باشید که برای فایلی به نام -print و ( و !... یا هر گزاره دیگر find، لازم است اطمینان حاصل کنید که نام فایل به صورت ./-print, ./(, ! یا سایر مسیرهای نسبی یا مطلقِ فایل عبور داده شود، که find سر در گم نگردد.
اگر bash شما با loadable builtin support کامپایل گردیده، میتوانید یک فایل finfo داخلی بسازید(در شاخه examples/loadables/ دایرکتوری فرعی درخت فایلهای منبع bash خودتان، تایپ کنید make)، آنرا enable کنید، و سپس کد زیر را به کار ببرید:
$ finfo -o .bashrc 644
آگاه باشید که finfo.c توزیع شده با bash تا نگارش 4.0 حداقل شامل یک باگ است(درگزینه -s )، بنابراین آشکارا کد زیاد بررسی نگردیده است. اکثر بستههای از پیش کامپایل شده bash شامل مثالهای کامپایل شده نیستند، از این جهت شاید این حایگزین برای اکثر کاربران دشوار باشد.
پرسش و پاسخ 87 (آخرین ویرایش 2013-07-25 13:37:13 توسط StephaneChazelas)
گاهی اوقات صرف نظر کردن از مستعارها (و توابع، از جمله توابع داخلی پوسته) سودمند است. به عنوان مثال، شاید در سیستم خود این تنظیم را داشته باشید:
alias grep='grep --color=auto'
اما گاهی اوقات، شما به انجام یک فرمان یک سطری به طرف لولهها نیاز دارید که در آنجا رنگها آشفتگی ایجاد میکنند. هر یک از موارد زیر را میتونید استفاده کنید:
unalias grep; grep ... #1 unalias -a; grep ... #2 "grep" ... #3 \grep ... #4 command grep ... #5
#1 قبل از استفاده از grep مستعار آن را عزل میکند، اگر grep مستعار نشده باشد، کاری انجام نمیدهد. به هر حال مستعار برای بقیه نشست آن پوسته نیز از بین رفته است.
#2 مشابه قبلی، اما تمام مستعارها حذف میشوند.
#3 و #4 یکسان هستند، به شما اجازه میدهند یکبار grep را در حالیکه از مستعار آن صرفنظر شده اجرا کنید، اما به طور کلی خیر.
#5 متمایز از دیگران است، در آن از مستعارها، توابع، و کلیدواژههای پوسته از قبیل time صرفنظر میشود. بازهم دستورات داخلی مانند echo نسبت به /bin/echo ترجیح داده میشوند. چند گزینه دارد که شاید بخواهید استفاده کنید -- help command را ببینید.
انتخاب #6 نوشتن تابع خودتان خواهد بود که وقتی ترمینال خروجی استاندارد نباشد رفتار نامطلوب مرتکب نمیشود. به این ترتیب:
ls() { if test -t 1; then command ls -FC "$@" else command ls "$@" fi }
استفاده از این تابع به جای alias ls='ls -FC' موقعی که تابع در یک خط لوله به کار میرود(یا هر وضعیت دیگر که در آن خروجی استاندارد ترمینال نیست) بعضی پرچمهای خاص را غیر فعال میکند.
برای بحث بیشتر در مورد استفاده از توابع به جای مستعارها پرسش و پاسخ شماره 80 را ببینید.
پرسش و پاسخ 86 (آخرین ویرایش 2009-08-30 21:09:35 توسط localhost)