یادداشت نویسنده

کدهای این پست را بر روی سیستم FreeBSD نوشته و تست کرده‌ام ولی بر روی سیستم‌های لینوکسی هم اجرا می‌شوند.

فهرست مطالب

"راهنمای دستور find در سیستم‌های شبه یونیکس"

مقدمه🔗

find یکی از مهمترین دستورات یونیکس و سیستم‌های شبه‌یونیکس مانند لینوکس است که دیر یا زود به آن نیاز مبرمی پیدا می‌کنید. با یادگیری دستور find و به کار بردن عملی آن در کارهای روزانه، هر روز می‌توانید کاربرد جدیدی برای آن پیدا کنید، مثلا با اتصال خروجی این دستور به ورودی دستوری دیگر از طریق pipe می‌توانید عملیات گوناگونی را روی فایل‌هایی که پیدا کرده‌اید انجام دهید و یا اینکه مستقیما از سوییچ -exec دستور find استفاده کنید. -exec بر روی تمام فایل‌های یافت شده اجرا خواهد شد و می‌تواند شامل هر دستوری باشد. وقتی که در find خبره شدید احتمالا به خود خواهید گفت: «بدون آن چگونه زندگی می‌کردم؟!»

طرح چند پرسش🔗

  • دنبال ویدیویی به نام moammaye_hasti.mp4 از شجریان می‌گردم ولی پیداش نمی‌کنم.
  • فیلم‌های سینمایی من همه بالای ۶۰۰-۷۰۰ مگابایت هستن ولی متاسفانه توی فولدرهای مختلف گم شدن. از همه بدتر اینکه اسمشون رو هم چیزهای الکی گذاشتم مثلا ddd.mp4 یا london.mp4
  • ماه گذشته دو تا سی دی کامل mp3 کپی کردم توی کامپیوتر ولی همه‌شون رو گم کردم. خدای من! شتر با بارش گم شد!
  • می‌خوام عکس‌های موجود در این دایرکتوری و زیر دایرکتورهاش رو پاک کنم ولی دایرکتوری خیلی بزرگ و پر از اطلاعاته. حتی اگه وقتش رو داشته باشم و به صورت دستی این کار رو انجام بدم، اون موقع همش ناراحت اینم که آیا همه عکس‌ها رو پاک کردم یا خدایی ناکرده چند تایی‌شون از دستم در رفته!
  • می‌خوام لیست کل فایل‌های این دایرکتوری و کلیه زیر دایرکتوری‌هاش رو به دست بیارم. ls کمکی بهم نکرد.
  • می‌خوام کلیه‌ی فایل‌هایی که owner شون reza است رو به نام mohsen در بیارم. می دونم که باید از chown استفاده کنم ولی از کجا همه‌ی فایل‌های رضا رو شناسایی کنم و چه طوری اون همه chown بنویسم؟

جواب این چند پرسش و بسیاری پرسش‌های دیگر دستور find است.

جستجوی کلیه‌ی فایل ها🔗

شکل کلی و ساده دستور find به صورت زیر است:

bash
$ find path experssion

path مسیر مورد جستجو است. جستجو در کلیه‌ی دایرکتوری‌های زیر مجموعه path به صورت تو در تو (recursive) و تا آخرین دایرکتوری انجام می‌شود مگر اینکه عمق جستجو را معلوم کرده باشیم که در این صورت، جستجو تا عمق انتخاب شده انجام خواهد شد. عمق جستجو با استفاده از سوییچ -d (ابتدای کلمه depth) مشخص می‌شود.

bash
$ find path -d 3 other-experssions

در دستور بالا find تا سه زیر دایرکتوری را جستجو می‌کند.

لیست کلیه‌ی فایل‌ها و دایرکتوری‌های خانه (و زیر دایرکتوری‌های آن)

bash
$ find ~

این دستور فاقد experssion است و به صورت اتوماتیک لیست فایل‌های یافت شده را در خروجی استاندارد چاپ می کند. ~ (تیلده) نشان دهنده‌ی دایرکتوری خانه کاربرِ جاری است.

جستجو بر مبنای نام فایل🔗

برای جستجو بر مبنای نام فایل از دو سوییچ -name و -iname استفاده می‌کنیم. حرف i در ابتدای سوییچ -iname از کلمه‌ی case-insensitive می‌آید به این معنا که کوچکی و بزرگی حروف اهمیتی ندارد. چنانچه در عبارتی که جلوی سوییچ‌ها نوشته می‌شود از wildcard استفاده نکرده باشیم، find دقیقا عبارت مورد نظر را جستجو می‌کند. در صورت استفاده از wildcard دستور find بر مبنای wildcard تصمیم گیری می‌کند. * به معنای هر تعداد کاراکتر و ? به معنای دقیقا یک کاراکتر است.

جستجو کردن فایل moammaye_hasti.mp3 در دایرکتوری خانگی (و زیر دایرکتوری‌های آن)

bash
$ find ~ -iname "moammaye_hasti.mp3"

برای اینکه جستجو، هم فایل‌های با حروف کوچک و هم فایل‌های با حروف بزرگ را شامل شود از سوییچ -iname استفاده می‌کنیم. اگر از اسم فایل اطمینان دارید می‌توانید نام را دقیقا مانند نام فایل بنویسید و از سوییچ -name استفاده کنید.

جستجو کردن کلیه‌ی فایل‌های mp3 علی

bash
$ find ~ali -iname "*.mp3"

جستجو کردن کلیه‌ی فایل‌های پنهان موجود در دایرکتوری جاری و زیر دایرکتوری‌های آن

bash
$ find . -name ".*"

می‌دانید که فایل پنهان فایلی است که با . (دات) شروع شود.

کلیه‌ی فایل‌ها و دایرکتوری‌های سه حرفی را نمایش بده. زیر دایرکتوری‌ها را جستجو نکن.

bash
$ find ~ -name "???" -d 1

فراموش نکنید چنانچه از * و یا ? استفاده می‌کنید حتما متن را با استفاده از "" (double quote) و یا ′′ (single quote) quote کنید و یا اینکه قبل از کاراکتر ویژه * و یا ? علامت \ قرار دهید و یا به عبارت دیگر آن را escape کنید.

ارسال خروجی find به xargs🔗

تا به حال با پیغام arguments too long مواجه شده اید؟ علت این پیغام چیست؟ پوسته یا shell تا حد معینی می‌تواند آرگومان‌های خط فرمان را به برنامه فراخوانده شده ارسال کند. وقتی این تعداد از حد معین شده توسط پوسته بیشتر می‌شود، پوسته پیغام arguments too long می‌دهد. برای حل این مشکل و البته برای کاربرد در وضعیت‌های دیگر دستور xargs به کمک می‌آید.

مثال زیر را ببینید:

bash
$ ls | xargs echo

xargs محتوای خوانده شده از ورودی استاندارد را به عنوان آرگومان به دستوری که در جلویش می‌آید می‌فرستد. در دستور فوق ورودی استاندارد به سمت pipe تغییر جهت یافته است و لذا xargs محتوای ارسالی در pipe، که همان خروجی دستور ls است را می‌خواند و آن را به عنوان آرگومان‌های ورودی به دستور echo ارسال می‌کند. xargs محدودیت تعداد آرگومان پوسته را می‌داند و لذا دیگر شاهد پیغام arguments too long نخواهید بود.

برای حذف کلیه‌ی فایل‌های mp3 از دایرکتوری جاری به شیوه زیر نیز می‌توان عمل کرد:

bash
$ ls *.mp3 | xargs rm

دستور ls کلیه‌ی فایل‌های خاتمه یافته به پسوند mp3 را لیست می‌کند و آن را به pipe می فرستد. xargs از pipe می‌خواند و محتوای خوانده شده را به عنوان آرگومان‌های دستور rm منظور می‌کند و سپس rm را اجرا می‌کند. اگر لیست فایل‌های mp3 بسیار زیاد باشد در این صورت xargs یکبار دستور rm را با تعداد آرگومان کافی خوانده شده از pipe اجرا می‌کند و سپس دوباره اقدام به اجرای دستور rm با آرگومان‌های جدید می‌کند. این عمل تا زمانی که pipe خالی شود ادامه می‌یابد.

دستور بالا را می‌توان با استفاده از find نوشت. توجه کنید چون find جستجو را به صورت تو در تو انجام می‌دهد برای پیاده سازی دقیق دستور بالا باید عمق جستجو را ۱ بگذاریم.

bash
$ find . -d 1 -iname "*.mp3" | xargs rm

عمق جستجو با سوییچ -d مشخص می‌شود.

نکته مهم

متاسفانه دستور xargs در مورد فایل‌هایی که در نامشان space دارند درست عمل نمی‌کند. ولی راه‌حل‌هایی برای رفع این مشکل وجود دارد.

حل مشکل space در xargs و find🔗

دستور xargs بر مبنای کاراکترهای space و \n کار می‌کند، یعنی اگر میان نام یک فایل space باشد xargs آن را به چند نام تقسیم می‌کند، چون کاراکتر جداکننده آن space است.

خوشبختانه find دارای سوییچی به نام -print0 است که در آخر تمام موارد یافته شده کاراکتر NULL را قرار می‌دهد و همچنین دستور xargs نیز دارای سوییچی به نام -0 است که کاراکتر جداکننده را به NULL تغییر می‌دهد.

دو دستور زیر و خروجی‌های آن‌ها را ملاحظه بفرمایید.

bash
$  find . -name "Untitled 1.odt" | xargs ls -l
ls: ./Untitled: No such file or directory
ls: 1.odt: No such file or directory

$ find . -name "Untitled 1.odt" -print0 | xargs -0 ls -l
-rw-r--r--  1 mohsen  wheel  31308 Apr  1 17:33 ./Untitled 1.odt

به نظر مشکل بزرگی می‌آمد ولی با همکاری دو دستور با هم مسئله حل شد.

جستجو بر مبنای نوع (type) فایل🔗

گاهی اوقات نیاز به جستجو در میان نوع خاصی از فایل‌ها داریم. برای این منظور از سوییچ -type به همراه کاراکترهای تعریف شده زیر استفاده می‌کنیم.

  • b : فایل ویژه از نوع block یا block special file
  • c : فایل ویژه از نوع character یا character special file
  • d : دایرکتوری
  • f : فایل معمولی
  • l : لینک
  • s : سوکت socket

لیست کلیه‌ی فایل‌های معمولی در دایرکتوری جاری و لیست کامل اطلاعات آن

bash
$ find . -type f -d 1 | xargs ls -l

لیست کلیه‌ی لینک‌ها در دایرکتوری خانگی (و زیر دایرکتوری‌های آن)

bash
$ find ~ -type l

لینک‌های موجود در دایرکتوری خانگی به کجا اشاره می کنند؟

bash
$ find ~ -d 1 -type l | xargs ls -l | awk '{print $9 " " $10 " " $11}'

علامت + و -🔗

علامت + قبل از عدد به معنای «بزرگتر از» یا «بیشتر از» است. علامت - قبل از عدد به معنای «کوچکتر از» یا «کمتر از» است.

  • عبارت -size -20M یعنی سایز فایل کمتر از ۲۰ مگابایت باشد و عبارت -mtime -2 یعنی تاریخ ویرایش فایل کمتر از ۲ روز گذشته باشد.
  • عبارت -size +100K یعنی فایل‌هایی که سایزشان بیشتر از ۱۰۰ کیلو بایت باشد و عبارت -atime +2 یعنی فایل‌هایی که تاریخ آخرین دسترسی به آنها بیشتر از ۲ روز گذشته باشد.

جستجو بر مبنای بزرگی (size) فایل🔗

برای جستجو بر مبنای سایز از سوییچ -size استفاده می‌کنیم. به دنبال -size یک عدد صحیح می‌آید. چنانچه این عدد بدون هیچ گونه کاراکتر بعدی باشد به معنای تعداد دقیق بلاک‌های اشغال شده توسط برنامه است. متاسفانه فضای هر بلاک برای برنامه‌های مختلف متفاوت است. مثلا در سیستم FreeBSD که من استفاده می‌کنم دستور ls -s که تعداد بلاک هر فایل را بر می‌گرداند هر بلاک را ۱۰۲۴ بایت حساب می‌کند در صورتیکه find هر بلاک را ۵۱۲ بایت لحاظ می‌کند.

bash
$ ls -ls 5.txt
4 -rw-r--r--  1 mohsen  wheel  1058 Apr  6 21:25 5.txt

دستور فوق نشان می‌دهد که فایل 5 دارای ۱۰۵۸ بایت است و ۴ بلاک اشغال کرده است. دستور ls بلاک‌ها را در واحدهای چهارتایی اختصاص داده است در حالیکه تعداد بلاک مصرف شده واقعی با حجم هر بلاک ۱۰۲۴ بایت دو بلاک است. ولی دستور find بلاک‌ها را ۵۱۲ بایت حساب می‌کند لذا عدد بلاک مصرف شده برای این فایل از نظر دستور find تعداد ۳ بلاک است. این موضوع را با دستور زیر تست می‌کنیم.

bash
$ find -size 3 | xargs ls -ls
4 -rw-r--r--  1 mohsen  wheel  1058 Apr  6 21:25 ./5.txt

موضوع پیجیده شد؟ خوشبختانه راه‌های فرار خوبی وجود دارد! می‌توان بعد از عدد صحیح، از کاراکترهای زیر استفاده کرد:

  • c : کاراکتر. مثلا 27c یعنی ۲۷ کاراکتر یا ۲۷ بایت
  • k : کیلو بایت. مثلا 27k یعنی ۲۷ کیلو بایت. (توجه کنید k با حرف کوچک نوشته شده است)
  • M : مگا بایت. مثلا 27M یعنی ۲۷ مگا بایت
  • G : گیگا بایت. مثلا 27G یعنی ۲۷ گیگا بایت
  • T : ترا بایت. مثلا 27T یعنی ۲۷ ترا بایت
  • P : پتا بایت. مثلا 27P یعنی ۲۷ پتا بایت

لیست فایل‌های MP4 که سایز آنها بیشتر از ۶۰۰ مگا بایت است:

bash
$ find ~ -type f -iname "*.mp4" -size +600M

لیست فایل‌های jpg که بین ۴۰۰ کیلو بایت تا ۵ مگابایت هستند:

bash
$ find ~ -type f -iname "*.jpg" -size +400k -size -5M

نکته مهم

دستور بالا نشان می‌دهد زمانی که دو یا چند شرط وجود داشته باشد تمامی شرط‌ها می‌بایست true باشند یا به عبارت دیگر شرط‌ها با هم AND می‌شوند.

جستجو بر مبنای سطح دسترسی (permission) فایل🔗

سوییچ -perm برای یافتن فایل با سطوح دسترسی مشخص استفاده می‌شود. عدد قرار گرفته در جلو سوییچ می‌بایست عددی در مبنای ۸ باشد.

فایل‌های دارای اجازه دسترسی ۷۷۷ را نمایش بده.

bash
$ find ~ -perm 777

فایل‌های دارای اجازه دسترسی ۷۵۵ را نمایش بده.

bash
$ find ~ -perm 755

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

نکته

علامت - پیش از عدد permission می‌گوید که اجازه دسترسی واقعی فایل را با عدد جلوی permission به صورت بیتی AND کن و اگر حاصل درست بود فایل را نمایش بده (یعنی آن فایل مجوزی که برای شما مهم بوده است را دارد.)

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

rwx rwx rwx
000 010 000

عدد هشت بیتی حاصل 020 است. از این عدد به صورت زیر استفاده می‌کنیم:

bash
$ find ~ -perm -020

لیست کامل فایل‌های اجرایی را نمایش بده.

rwx rwx rwx
001 000 000

عدد هشت بیتی حاصل 001 است.

bash
$ find ~ -type f -perm -001

سوییچ -perm همچنین قابلیت شناسایی بیت‌های SUID و SGID و sticky bit را نیز دارد. برای این منظور فقط کافیست ترکیب SUID SGID sticky را در سمت چپ rwx صاحب فایل تشکیل دهید و این بار عدد ۱۲ بیتی حاصل را سه رقم سه رقم در مبنای هشت بنویسید و با علامت - بعد از سوییچ -perm قرار دهید.

تمام فایل‌هایی که بیت SUID آن فعال است را پیدا کن.

SUID/SGID/sticky rwx rwx rwx
000 000 000 0 0 1

عدد ۱۲ بیتی حاصل 4000 است.

bash
$ find /usr/bin -type f -perm -4000 -print0 | xargs -0 ls -lh | head -n 5
-r-sr-xr-x  4 root  wheel     22k Dec  4  2012 /usr/bin/at
-r-sr-xr-x  4 root  wheel     22k Dec  4  2012 /usr/bin/atq
-r-sr-xr-x  4 root  wheel     22k Dec  4  2012 /usr/bin/atrm
-r-sr-xr-x  4 root  wheel     22k Dec  4  2012 /usr/bin/batch
-r-sr-xr-x  6 root  wheel     18k Dec  4  2012 /usr/bin/chfn

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

جستجو بر مبنای زمان ویرایش ، زمان دسترسی و زمان تغییر i-node🔗

به طور کلی جستجوی زمانی را بر مبنای دو پارامتر روز و دقیقه انجام می‌دهیم.

جستجو بر مبنای روز🔗

سوییج‌های جستجو بر مبنای روز به صورت زیر تعریف می‌شوند:

  • -mtime : برگرفته از عبارت modified time یا زمان آخرین ویرایش فایل. از آنجایی که یونیکس زمان ایجاد فایل را ذخیره نمی‌کند به عنوان زمان ایجاد فایل هم می‌تواند استفاده شود.
  • -atime : برگرفته از عبارت accessed time یا زمان آخرین دسترسی به فایل.
  • -ctime : برگرفته از عبارت i-node change time یا زمانِ آخرین تغییرِ اطلاعات i-node. چنانچه نام فایل، صاحب فایل، گروه و یا تعداد لینک‌های داده شده به فایل و ... تغییر کند بدون اینکه محتوای فایل مورد ویرایش قرار گیرد این تاریخ، زمانی جلوتر از زمان آخرین ویرایش فایل خواهد داشت.

فایل‌هایی که ۷ روز پیش ویرایش شده‌اند را نمایش بده.

bash
$ find ~ -mtime 7

همین مثال می‌تواند به صورت زیر با استفاده از علامت های + و - نیز نوشته شود.

bash
$ find ~ -mtime +6 -mtime -7

یعنی زمان ویرایش فایل بیشتر از ۶ روز و کمتر از ۷ روز پیش باشد. علامت + شامل همان عدد نمی شود ولی علامت - خود عدد را نیز شامل می‌شود.

فایل‌هایی که بیشتر از ۳۰ روز است که از آنها استفاده نکرده‌ام را نمایش بده.

bash
$ find ~ -atime +30 -type f

نمایش فایل‌هایی که بیشتر از یک روز پیش و کمتر از دو روز پیش به آن‌ها دسترسی داشته‌ام.

bash
$ (echo -e current date :  `date` "\n";find . -type f -d 1 -atime -2 -atime +1 -print0 | xargs -0 ls -lut)

current date :  Fri Apr 11 16:15:37 IRDT 2014 

-rw-r--r--  1 mohsen  wheel      11710 Apr  9 23:20 ./b.result
-rw-r--r--  1 mohsen  wheel  155845071 Apr  9 23:13 ./download36.mp4
-rw-r--r--  1 mohsen  wheel    1292918 Apr  9 18:01 ./b.flv
-rw-r--r--  1 mohsen  wheel    2168490 Apr  9 18:01 ./recovered2.flv
-rw-r--r--  1 mohsen  wheel    3022870 Apr  9 18:00 ./recovered1.flv

همانطوری که مشاهده می‌کنید علامت + شامل خود عدد (در اینجا ۱ روز) نمی‌شود ولی علامت - شامل عدد (در اینجا ۲ روز) می‌شود.

جستجو بر مبنای دقیقه🔗

سه سوییچ زیر برای جستجو بر مبنای دقیقه مورد استفاده قرار می‌گیرند:

  • -mmin : برگرفته از عبارت modified minute
  • -amin : برگرفته از عبارت accessed minute
  • -cmin : برگرفته از عبارت i-node change minute

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

فایل‌هایی که کمتر از ۶۰ دقیقه قبل ویرایش شده‌اند را نمایش دهید.

bash
$ find . -type f -mmin -60

فایل‌هایی که کمتر از ۴۰ دقیقه پیش آن‌ها را مشاهده کرده‌ام را نمایش دهید.

bash
$ find . -type f -amin -40

جستجوی زمانی بر مبنای زمان ویرایش یک فایل🔗

اگر می‌خواهید متوجه شوید زمان آخرین ویرایش چه فایل‌هایی از فایل داده شده کمتر است از سوییچ -newer استفاده کنید.

چه فایل‌هایی از فایل tmp.sh جدیدتر هستند؟

bash
$ ls -l tmp.sh
-rwxr-xr-x  1 mohsen  wheel  34 Mar 24 21:56 tmp.sh*

$ find . -d 1 -newer tmp.sh | xargs ls -lt | head -n 5
-rw-r--r--  1 mohsen  wheel   0 Apr 11 19:05 ./4
-rw-r--r--  1 mohsen  wheel   0 Apr 11 19:05 ./3
-rwxr-xr-x  2 mohsen  wheel   2 Mar 30 22:40 ./1
-rwxr-xr-x  2 mohsen  wheel   2 Mar 30 22:40 ./2
lrwxr-xr-x  1 mohsen  wheel  21 Mar 30 17:08 ./.profile -> /home/mohsen/.profile

جستجو بر مبنای صاحب (owner) و گروه (group) فایل🔗

از سوییچ -user برای جستجوی فایل‌ها بر مبنای نام کاربری یا شماره‌ی یکتای صاحب فایل استفاده می‌شود. شماره یکتای هر کاربر را می‌توانید در فایل etc/passwd/ مشاهده کنید.

bash
$ cat /etc/passwd | grep ^mohsen
mohsen:*:1001:1001:Mohsen Safari:/home/mohsen:/usr/local/bin/bash

مثال بالا خط مربوط به کاربر mohsen در فایل etc/passwd/ را نشان می‌دهد که دارای شماره کاربری ۱۰۰۱ است.

از سوییچ -group برای جستجوی فایل‌ها بر مبنای نام گروه و یا شماره‌ی یکتای گروه استفاده می‌شود. شماره یکتای هر گروه را می‌توانید در فایل etc/group/ مشاهده کنید.

bash
$ cat /etc/group | grep ^mohsen 
mohsen:*:1001:

در مثال بالا مشاهده می‌کنید که گروه mohsen دارای شماره یکتای ۱۰۰۱ است.

کلیه‌ی فایل‌هایی که SGID آن ها به wheel ست شده است را پیدا کنید.

bash
$ find ~ -group wheel -perm -2000

جستجو بر مبنای i-number🔗

تا به حال دستور ls -li را اجرا کرده‌اید؟ نمونه خروجی این دستور را ببینید:

bash
$ ls -li
total 64
11397272 drwxr-xr-x  6 mohsen  wheel   512 Apr  5 20:23 ./
11318506 drwxr-xr-x  3 mohsen  wheel   512 Mar 21 21:34 ../
11396871 lrwxr-xr-x  1 mohsen  wheel    21 Mar 30 17:08 .profile@ -> /home/mohsen/.profile
11396876 -rw-r--r--  2 mohsen  wheel     2 Mar 30 22:40 1
11396876 -rw-r--r--  2 mohsen  wheel     2 Mar 30 22:40 2
11396865 -rwxr-xr-x  1 mohsen  wheel    34 Mar 24 21:56 2.2.sh*
11879926 drwxr-xr-x  3 mohsen  wheel   512 Mar 30 18:12 DIR/
11396862 -rw-r--r--  1 mohsen  wheel   252 Mar 24 20:51 args-no.sh
11396866 -rwxr-xr-x  1 mohsen  wheel  2054 Mar 23 16:02 cleanup*
12039467 drwxrwxr-x  2 mohsen  wheel   512 Apr  5 20:23 d1/
12039468 dr-xr-xr-x  2 mohsen  wheel   512 Apr  5 20:23 d2/
12039469 drwxr-xr-x  2 mohsen  wheel   512 Apr  5 20:23 d3/
11396867 -rw-r--r--  1 mohsen  wheel   352 Mar 24 21:49 log
11396875 -rwxr-xr-x  1 mohsen  wheel  8780 Mar 24 20:59 readme*
11396878 -rwxr-xr-x  1 mohsen  wheel   403 Mar 21 22:11 swap.sh*

عددهای قرار گرفته در سمت چپ هر سطر i-number نامیده می‌شوند و شماره درایه مربوط به آن فایل در داخل جدول i-table یا inode-table است.

دستور find قابلیت جستجو بر مبنای i-number را دارد. برای این منظور می‌توانید از سوییچ -inum استفاده کنید. می‌بایست بعد از -inum شماره i-number فایل مورد نظر را قرار دهید.

فایل دارای i-number به شماره ۱۱۳۹۶۸۶۶ را از طریق دستور find بیابید.

bash
$ find . -inum 11396866 | xargs ls -li
11396866 -rwxr-xr-x  1 mohsen  wheel  2054 Mar 23 16:02 ./cleanup

برای مشاهده صحت عملکرد find خروجی آن را از طریق pipe به دستور xargs ls -li ارسال میکنیم. سوییچ -i در دستور ls همانگونه که می‌دانید شماره i-number هر فایل را بر می‌گرداند.

جستجوی فایل‌های خالی (empty)🔗

از سوییچ -empty برای یافتن فایل‌های خالی استفاده می‌شود.

bash
$ >1 ; >2 ; >3
$ echo "salam" >4
$ find . -empty | xargs ls -l
-rw-r--r--  1 mohsen  wheel  0 Apr  7 15:32 ./1
-rw-r--r--  1 mohsen  wheel  0 Apr  7 15:32 ./2
-rw-r--r--  1 mohsen  wheel  0 Apr  7 15:32 ./3

همانطور که در ریزِ لیست فایل‌ها می‌بینید حجم تمام این فایل‌ها صفر بایت است و در واقع خالی هستند.

از سوییچ -empty برای یافتن دایرکتوری‌های خالی هم می‌توان استفاده کرد.

bash
$ mkdir newdir
$ echo "salam" >newdir/1.txt
$ mkdir emptydir
$ find . -type d -empty
./emptydir

سوییچ exec- و ok-🔗

تاکنون برای ارسال خروجی دستور find به برنامه‌های دیگر از pipe و xargs استفاده کرده‌ایم ولی این شیوه تنها راه‌حل نیست بلکه می‌توانیم از سوییچ -exec خود دستور find هم استفاده کنیم. بعد از -exec هر دستوری می‌تواند بیایید.

  1. سوییچ -ok دقیقا مانند سوییچ -exec است با این تفاوت که قبل از اجرای دستور از شما می‌پرسد که آیا دستور را اجرا کند یا خیر.

  2. متغیر {} در سوییچ -exec به معنای نام فایل یافته شده است. با یافتن نتیجه جدید مقدار {} رونویسی می‌شود.

  3. -exec یکبار به ازای هر فایل یافته شده اجرا می‌شود به عنوان مثال اگر جستجویی ۴۰ نتیجه داشته باشد و در این جستجو از سوییج -exec استفاده کرده باشیم، -exec چهل بار اجرا خواهد شد، در صورتیکه با استفاده از pipe و xargs، دستور xargs چهل نتیجه یافته شده را به عنوان آرگومان دستور بعدی منظور کرده و به احتمال بسیار زیاد فقط یکبار دستور مورد نظر را اجرا می کند. از طرف دیگر xargs در مورد فایل‌هایی که در داخل نامشان space دارند به صورت پیش فرض درست عمل نمی کند (مشاهده راه حل) و در این مواقع بهتر است از سوییچ -exec استفاده کنید. تصمیم گیری در مورد استفاده از xargs یا سوییچ -exec با توجه به شرایط و تجربه با شما خواهد بود .

  4. بعد از سوییج -exec دستوری می‌آید که در صورت پیدا شدن یک فایل اجرا خواهد شد. از آنجا که این دستور باید مستقیما و به طور کامل به دستور find ارسال شود تا در موقع لزوم اجرا شود، می‌بایست کلیه‌ی کاراکترهایی که shell آن‌ها را تفسیر می کند را escape کنیم. از جمله‌ی این کاراکترها ; و * و ? هستند. برای escape کردن این کاراکترها قبل از آنها یک \ قرار دهید. از طرفی find می‌بایست نقطه پایان دستور -exec را شناسایی کند. برای نشان دادن نقطه پایان دستور -exec از; استفاده می‌کنیم. ; در پوسته به معنای جداکننده دستورها است لذا ; نیز می‌بایست escape شود.

فایل‌های mp3 دایرکتوری جاری و زیردایرکتوری‌های آن را حذف کنید. لطفا قبل از حذف از من سوال کنید.

bash
$ find . -iname "*.mp3" -ok rm {} \;

دستور فوق فایل‌های mp3 را یافته و بعد از پرسیدن اینکه آیا دستور اجرا شود یا خیر، در صورت موافقت، دستور rm را روی فایل مورد نظر ({}) اجرا می‌کند. -ok به ازای هر نتیجه یکبار از شما می‌پرسد که آیا دستور را اجرا کند یا خیر. بنابر این شما می‌توانید دستور را روی بعضی از فایل‌ها اجرا کنید و روی بعضی فایل‌ها اجرا نکنید.

از کلیه‌ی فایل‌های txt در دایرکتوری فعلی backup بگیرید.

bash
$ find . -d 1 -iname "*.txt" -exec mv {} {}.bak \;

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

bash
$ ls -li
12039479 -rw-r--r--  1 mohsen  wheel  0 Apr  7 16:02 o      o

ساده‌ترین راه استفاده از دستور find به همراه سوییچ‌های -inum و -exec است.

bash
$ find . -inum 1203979 -exec rm {} \;

شیوه بالا برای انجام هرگونه عملیات روی فایل‌هایی که دارای کاراکترهای ویژه هستند و یا اینکه نامشان در ترمینال به درستی نمایش داده نمی‌شود مناسب است.

مجوز کلیه‌ی فایل‌های دایرکتوری جاری که نامشان با عدد شروع می‌شود را به ۷۵۵ تغییر دهید.

bash
$ find . -d 1 -name "[0-9]*" -type f -exec chmod 755 {} \;

استفاده از AND و OR و NOT🔗

به صورت پیش فرض کلیه‌ی شرط‌های موجود در دستور find با هم AND می‌شوند ولی ما می‌توانیم با استفاده از چند سوییچ و عملگرِ ! روند پیش فرض find را تغییر دهیم.

توجه

عملگرِ ! قبل از شرط، به معنای نقیض شرط مشخص شده است.

فایل‌هایی که بزرگتر از ۳۴ کاراکتر نیستند را نمایش بدهید.

bash
$ find . -d 1 -size -34c
$ find . -d 1 ! -size +34c

دو دستور فوق تاحدودی معادل یکدیگرند. دستور اول می‌گوید سایز فایل کمتر از ۳۴ کاراکتر باشد و دستور دوم می‌گوید سایز فایل بیشتر از ۳۴ کاراکتر نباشد ولی دستور اول فایل‌های ۳۴ کاراکتری را نشان نمی‌دهد ولی دستور دوم نشان می‌دهد.

سوییچ -a به معنای AND و سوییچ -o به معنای OR است . شرط‌ها را می‌توانیم با پرانتز گروه‌بندی کنیم. فراموش نکنید به علت اینکه پرانتز برای پوسته معنا دارد آنرا حتما با \ (بک اسلش) escape کنید.

فایل‌هایی که در اسمشان حسن یا حسین است را پیدا کنید.

bash
$ find . \( -iname "*hassan*" -o -iname "*hossein*" \)

دستور فوق می‌تواند به صورت زیر نیز نوشته شود.

bash
$ find . -iname "*hassan*" -print -o -iname "*hossein*" -print

فایل‌هایی که دیگران اجازه هیچ گونه دسترسی به آن‌ها را ندارند پیدا کنید.

bash
$ find . -d 1 ! \( -perm -001  -o -perm -002  -o -perm -004 \)  -print0 | xargs -0 ls -ld
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./1
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./2
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./3
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./4

دستور فوق می‌تواند به صورت زیر نیز نوشته شود:

bash
$ find . -d 1 ! -perm 001 ! -perm 002 ! -perm -004 -print0 | xargs -0 ls -ld
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./1
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./2
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./3
-rwxr-x---  1 mohsen  wheel  0 Apr 12 16:10 ./4

پاسخ پرسش های مطرح شده🔗

پیدا کردن ویدیوی moammaye_hasti.mp4

bash
$ find ~ -type f -iname "moammaye_hasti.mp4"

یافتن ویدیوهای حجیم با حجم بالای ۶۰۰ مگا بایت

bash
$ find ~ -type f -size +600M

پیدا کردن mp3 هایی که ماه گذشته ریخته بودم توی کامپیوتر. فکر می‌کنم بین ۳۰ تا ۳۵ روز گذشته بود؟!

bash
$ find ~ -type f -iname "*.mp3" -ctime +29 -ctime -35

حذف کردن عکس‌ها. عکسهام همه پسوند jpg دارند. اگر هم نداشته باشند راحتترم که چند بار دستور find رو برای حذف پسوندهای مختلف صدا بزنم.

bash
$ find ~/doc/BBAAB‌‌B -type f -iname "*.jpg" -exec rm {} \;

لیست کل فایل‌های دایرکتوری خانه و زیر دایرکتوری‌های آن. جایی که ls به راحتی کمک نمی‌کند!

bash
$ find ~ -type f

فایل‌های رضا را به محسن تغییر مالکیت بده.

bash
$ find ~ -user reza -ok chown mohsen {} \;

دستور بالا به شرطی اجرا خواهد شد که مجوزهای لازم را داشته باشید.

پایان مطلب و نتیجه‌گیری🔗

نوشتن این مطلب سخت بود ولی خوشحالم که آن را نیمه کاره رها نکردم. حجم بالای اطلاعات که می‌بایست به مخاطب منتقل کرد و «خوب» و «صحیح» منتقل کرد کار نوشتن و بازنگری متن را زمان‌بر و طولانی کرد. حالا شاید بتوانم ادعا کنم که برای ۹۰ درصد کاربران روزمره‌ی سیستم‌های شبه یونیکس این راهنما می‌تواند آخرین راهنمای دستور find باشد. ولی امیدوارم که به این مطلب بسنده نکنید و متوقف نشوید. نوشته‌های دیگر را بخوانید و از مستندات find که در خط فرمان به صورت زیر قابل دسترسی است غافل نباشید:

bash
$ man find

چنانچه در کدها و یا توضیحات خطایی می‌بینید سپاسگزار می‌شوم کامنت بگذارید و یا به هر طریق دیگری اطلاع دهید تا آن را اصلاح کنم.

شاد باشید.

نوشته شده در: 1402-02-08 (1 سال 6 ماه 3 هفته پیش)

من محسن هستم؛ برنامه‌نویس سابق PHP و Laravel و Zend Framework و پایتون و فلسک. تمرکزم بیشتر روی لاراول بود! الان از صفر مشغول مطالعات اقتصادی هستم.

برای ارتباط با من یا در همین سایت کامنت بگذارید و یا به dokaj.ir(at)gmail.com ایمیل بزنید.

در مورد این مطلب یادداشتی بنویسید.