منبع اصلی نوشتار زیر در این لینک قرار دارد

حفظ تمام سطرهای فایل history


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

این روش برای آن طراحی شده تا به شما امکان بدهد ثبت وقایع کامل دوستانه‌ای از تمام فرمانهای اجرا شده توسط کاربر را ذخیره کنید، مقصود از آن حسابرسی امن فرمانها نمی‌باشد -صفحه امنیت bash در برابر پاکسازی تاریخچه را ببینید.

به طور پیش‌فرض، Bash تاریچه‌اش رافقط موقع خروج به روزرسانی می‌کند، و تاریخچه موجود را با یک نگارش جدیدتر بازنویسی می‌کند. این مطلب به دو دلیل شما را از داشتن یک فایل ثبت وقایع تاریخچه محروم می‌کند:

  • اگر کاربری در چندین نوبت به سیستم وارد شود(login)، رونویسی تضمین خواهد نمود که فقط آخرین پوسته تاریخچه‌اش هنگام خروج ذخیره خواهد شد.
  • اگر پوسته شما به طور غیرعادی خاتمه یابد - برای مثال به دلیل مشکلات شبکه، تغییرات فایروال، یا به علت اینکه kill گردیده است -تاریخچه‌ای نوشته نخواهد شد.

برای حل مشکل نخست، ما گزینه 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) به عنوان تمرین برای خواننده باقی گذاشته شد.


CategoryShell

پرسش و پاسخ 88 (آخرین ویرایش ‎2013-07-21 20:03:09‎ توسط ppp091138132034)