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

Bash GUI for wget

اینبار هم میخوام بازم یه اسکریپت رو که تازه نوشتم معرفی کنم البته میخوام یه کم با توضیح بیشتر این رو بنویسم، شاید به درد کسی خورد. zenity یه برنامه سادست که به شما امکان ایجاد یک سری دیالوگ از طریق خط فرمان رو میده،‌البته این سری فقط با یه دیالوگ از این سری کار داریم، ولی اگه وقت بشه در آینده ای نه چندان دور (همین ماه تیر) درباره این برنامه با یه سری مثال، کاملتر مینویسم.
برنامه wget رو احتمالا میشناسید، امکان دانلود یک فایل رو به شما میده و با کمک اون میتونید خیلی کارهای دیگه (از قبیل دانلود یه سایت به صورت کامل) رو انجام بدید. این برنامه به صورت خط فرمانیه و برای استفاده از اون باید از ترمینال استفاده کنید. قصد دارم اسکریپتی بنویسم که این برنامه دانلود رو به صورت GUI داشته باشیم و نه به صورت خط فرمان، فقط با استفاده از bash .

اولین بار توی فروم اوبونتو به این کد برخوردم (خودتون فروم رو ببینید، من همین راه حل رو اونجا هم اضافه کردم) ولی اون یه سری مشکلات داشت، منجمله اینکه تا پایان دانلود صبر میکرد، یک دفعه میپرید آخر، اگه کاربر دکمه لغو رو میزد درست عمل نمیکرد، راه حلهای ارایه شده هم زیاد جالب نبودن.
این شد که من این اسکریپت رو نوشتم:

DOWNLOAD() {
  rand=&quot;$RANDOM <code>date</code>&quot;
  pipe=&quot;/tmp/pipe.<code>echo '$rand' | md5sum | tr -d ' -'</code>&quot;
  mkfifo $pipe
  wget -c $1 2&gt;&amp;1 | while read data;do
    if [ &quot;<code>echo $data | grep '^Length:'</code>&quot; ]; then
      total_size=<code>echo $data | grep &quot;^Length:&quot; | sed 's/.*\((.*)\).*/\1/' |  tr -d '()'</code> 
    fi
    if [ &quot;<code>echo $data | grep '[0-9]*%' </code>&quot; ];then 
      percent=<code>echo $data | grep -o &quot;[0-9]*%&quot; | tr -d '%'</code> 
      current=<code>echo $data | grep &quot;[0-9]*%&quot; | sed 's/\([0-9BKMG.]\+\).*/\1/' </code> 
      speed=<code>echo $data | grep &quot;[0-9]*%&quot; | sed 's/.*\(% [0-9BKMG.]\+\).*/\1/' | tr -d ' %'</code> 
      remain=<code>echo $data | grep -o &quot;[0-9A-Za-z]*$&quot; </code> 
      echo $percent
      echo &quot;#Downloading $1\n$current of $total_size ($percent%)\nSpeed : $speed/Sec\nEstimated time : $remain&quot;
    fi
  done &gt; $pipe &amp;

  wget_info=<code>ps ax |grep &quot;wget.*$1&quot; |awk '{print $1&quot;|&quot;$2}'</code>
  wget_pid=<code>echo $wget_info|cut -d'|' -f1 </code>

  zenity --progress --auto-close --text=&quot;Connecting to $1\n\n\n&quot; --width=&quot;350&quot; --title=&quot;Downloading&quot;&lt; $pipe
  if [ &quot;<code>ps -A |grep &quot;$wget_pid&quot;</code>&quot; ];then
    kill $wget_pid
  fi
  rm -f $pipe
}

if [ $1 ];then
  DOWNLOAD &quot;$1&quot;
else
  dllink=$(zenity --entry --text &quot;Your download link :&quot; --width=&quot;350&quot; --entry-text &quot;&quot; --title=&quot;Download url&quot;)
  if [ $dllink ];then
    DOWNLOAD &quot;$dllink&quot;
  fi
fi

توی این اسکریپت به جای استفاده از پایپهای بینام (Anonymus pipe ) از پایپهای نامدار (Named pipe ) استفاده شده. دلیلش هم اینه که قراره wget اجرا بشه، بعد خروجی wget پردازش بشه، بعد بر اساس خروجی اون، پروگرس بار روی دیالوگ به روز رسانی بشه. تا اینجا رو توی فروم اوبونتو هم ببینید درست انجام میشه. اما اگه بخوایم کاربر قابلیت لغو این عملیات رو داشته باشه – با بستن دیالوگ یا زدن دکمه لغو – اونوقت دچار مشکل میشیم. یا باید مثل اون روش ارایه شده، تو یه حلقه اینو بررسی کنیم که هم دقیق نیست و هم اینکه بیخودی کلی از منابع سیستم مصرف میکنه. (بر خلاف اونچه نوشته شده توی اون پست فروم توی zenity پارامتر –auto-kill باگ نیست، و درست کار میکنه ولی برای این مورد مناسب نیست چون wget پدر پروسه zenity نیست که zenity اونو ببنده! )
تو این روش، wget اجرا میشه، خروجیش بررسی میشه و نهایتا تو یه پایپ نوشته میشه. کاری هم به بقیه چیزها نداره. از این طرف zenity اجرا میشه و از توی پایپ اطلاعات رو میخونه. اگه پایپ بی نام باشه، ما نمیتونیم اول pid پروسه wget رو بگیریم چون توی پایپ بینام، مجبوریم همون لحظه از پایپ استفاده کنیم که اجازه نمیده دستوراتی بینابین این دو تا بنویسیم. اما با این روش میتونیم اول wget رو اجرا کنیم،‌pid اونو ذخیره کنیم بعد بریم سراغ zenity . برای ایجاد پایپ یه اسم رندوم ایجاد شده (۲ خط اول تابع DOWNLOAD)‌ و بعد با mkfifo پایپ ایجاد شده، آخر تابع هم پایپ با دستور rm بسته شده.

حالا wget اجرا شده(خط ۵)، از روی خروجیش توی یک حلقه همه اطلاعات جمع آوری شده (که شامل اندازه فایل دانلودی خط ۷ – درصد دانلود شده خط ۱۰ ، حجم دانلود شده خط ۱۱ سرعت دانلود و زمان باقیمانده خطوط ۱۲ و ۱۳. در نهایت این اطلاعات در خروجی چاپ شدن که خروجی همون پایپه که در همین زمان یا با فاصله بسیار کمی zenity در حال خوندن این خروجی از پایپ هستش. فقط توی zenity وقتی به صورت پروگرس بار اجرا میشه، اگه تو ورودیش یه عدد بزنی (بین ۰ تا ۱۰۰ ) در صد پروگرس بار رو تغییر میده به اون عدد و اگه هر چیزی با یه # در ابتداش بزنی،‌ متن بالای پروگرس بار رو عوض میکنه.خط ۱۴ وظیفه نوشتن درصد تمام شده در پایپ که اینجا ورودی zenity هست رو به عهده داره و خط ۱۵ هم اطلاعات رو به روز میکنه. خطوط ۱۹ و ۲۰ کارشون گرفتن pid پروسه wget هست و با این روش،‌ما میتونیم مطمئن باشیم که پروسه اشتباهی رو پیدا نکردیم، فقط مگه روی همون سیستم در همون لحظه یه wget دیگه در حال دانلود همون فایل باشه که در اونصورت ممکنه اون wget انتخاب بشه (که خوب یه کم دور از ذهنه) و خط ۲۲ هم zenity اجرا شده،‌که با علامت کوچکتر (علامت رو که میزنم تو متن قاطی میشه، تو کروم سروته نشونش میده ;) ) از توی پایپ میخونه. بعد از اتمام zenity به هر دلیلی، بررسی میکنیم (با همون pid که ذخیره شده)‌ ببینیم wget در حال اجرا هست یا نه، اگه بود اون رو هم میبندیم.
البته اینو به صورت تابع نوشتم که اگه بخواید بتونید توی اسکریپتهای خودتون استفاده کنید. خودم هم یه اسکریپت کوچیک واسه استفاده از این تابع نوشتم ( ۷ یا ۸ خط پایانی ) که اگه اسکریپت با یه اٰگومان فراخوانی بشه اون آرگومان رو دانلود میکنه وگرنه یه دیالوگ (بازم zenity ) نشون میده و لینک رو میگیره.
بعد از این کار این اسکریپت رو توی یه جایی ذخیره کنید،‌ اون رو اجرایی کنید ، مثلا با کلیک راست بر روی فایل و انتخاب گزینه “Allow executing file as program ” توی زبانه “Permissions” یا با اجرای دستور :

chmod a+x /path/to/your/file

بعد هم اجراش کنید، هرجوری که دوست دارید.



برچسب ها : , , , , , , , ,