فصل ۱۷ – جستوجوی فایلها
وقتی در سیستم لینوکس خود گردش کردهایم، یک نکته کاملاً روشن شده است:
یک سیستم لینوکس معمولی تعداد بسیار زیادی فایل دارد!
و این پرسش پیش میآید: «چطور بین این همه فایل چیزی را که میخواهیم پیدا کنیم؟»
ما میدانیم که ساختار فایلسیستم لینوکس طبق اصولی منظم و ریشهدار از سیستمهای یونیکس شکل گرفته،
اما تعداد زیاد فایلها ممکن است پیدا کردن یک فایل خاص را بسیار دشوار کند.
در این فصل، با دو ابزار اصلی برای جستوجوی فایلها در سیستم آشنا میشویم:
- locate – جستوجو بر اساس نام فایل
- find – جستوجو در سلسلهمراتب دایرکتوریها
همچنین با دستوری آشنا میشویم که معمولاً برای پردازش خروجی دستورات جستوجو به کار میرود:
- xargs – ساخت و اجرای خط فرمانها از ورودی استاندارد
در کنار آن، دو دستور کمکی دیگر نیز معرفی میکنیم که برای بررسی و کار با فایلها مفیدند:
- touch – تغییر زمانهای فایل
- stat – نمایش وضعیت فایل یا فایلسیستم
locate – سادهترین روش برای پیدا کردن فایل
برنامهی locate یک جستوجوی بسیار سریع درون پایگاه دادهای از مسیر فایلها (pathnames) انجام میدهد
و هر نامی را که شامل رشتهی مورد نظر باشد، نمایش میدهد.
فرض کنید میخواهیم تمام برنامههایی را پیدا کنیم که نامشان با «zip» شروع میشود.
از آنجا که به دنبال برنامهها هستیم، میتوانیم حدس بزنیم که مسیر آنها در دایرکتوریهایی است که به bin/ ختم میشوند.
پس میتوانیم اینطور از locate استفاده کنیم:
[me@linuxbox ~]$ locate bin/zip
خروجی چیزی شبیه این خواهد بود:
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
اگر جستوجوی ما ساده نباشد، میتوانیم locate را با ابزارهای دیگر مثل grep ترکیب کنیم
تا جستوجوهای دقیقتر و پیشرفتهتری بسازیم:
[me@linuxbox ~]$ locate zip | grep bin
خروجی:
/bin/bunzip2
/bin/bzip2
/bin/bzip2recover
/bin/gunzip
/bin/gzip
/usr/bin/funzip
/usr/bin/gpg-zip
/usr/bin/preunzip
/usr/bin/prezip
/usr/bin/prezip-bin
/usr/bin/unzip
/usr/bin/unzipsfx
/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
برنامهی locate از سالها پیش در لینوکس وجود داشته و نسخههای مختلفی از آن ساخته شده است.
در توزیعهای امروزی دو نسخهی رایجتر وجود دارد:
- slocate
- mlocate
اما معمولاً هر دوی آنها از طریق یک لینک نمادین به نام locate فراخوانی میشوند.
هر نسخه مجموعهای از گزینهها و قابلیتهای مشترک دارد،
و برخی نسخهها از عبارات منظم (Regular Expressions) و الگوهای wildcard پشتیبانی میکنند
(که در فصلهای بعدی دربارهی آنها صحبت خواهیم کرد).
برای اطلاع از نسخهی نصبشدهی locate و گزینههای آن، میتوانید دستور زیر را اجرا کنید:
man locate
پایگاه دادهٔ locate از کجا میآید؟
شاید متوجه شده باشید که در بعضی توزیعها، بلافاصله بعد از نصب سیستم، دستور locate کار نمیکند؛
اما اگر روز بعد دوباره امتحان کنید، ناگهان درست کار میکند.
علت چیست؟
پایگاه دادهای که locate از آن استفاده میکند، توسط برنامهای به نام updatedb ساخته میشود.
این برنامه معمولاً بهصورت خودکار و زمانبندیشده (cron job) اجرا میشود — یعنی وظیفهای که در فواصل زمانی منظم توسط سرویس cron انجام میگیرد.
در اکثر سیستمهایی که locate نصب شده، برنامهی updatedb روزی یکبار اجرا میشود.
از آنجا که این پایگاه داده بهصورت لحظهای بهروزرسانی نمیشود، فایلهایی که بهتازگی ایجاد شدهاند ممکن است در نتایج locate دیده نشوند.
برای رفع این محدودیت، میتوانید خودتان بهصورت دستی پایگاه داده را بهروزرسانی کنید.
کافی است وارد حالت کاربر ریشه (superuser) شوید و دستور زیر را اجرا کنید:
# updatedb
find – روش دقیقتر (و سختتر) برای پیدا کردن فایلها
در حالی که locate فقط بر اساس نام فایل جستوجو میکند،
دستور find دایرکتوری مشخصی (و زیرشاخههای آن) را بر اساس مجموعهای از ویژگیها و شروط مختلف جستوجو میکند.
find ابزاری بسیار قدرتمند است و در فصلهای آینده، هنگام یادگیری مفاهیم برنامهنویسی شل،
بارها با آن سر و کار خواهیم داشت.
در سادهترین حالت، برای استفاده از find فقط کافی است مسیر دایرکتوری مورد نظر را مشخص کنیم.
مثلاً برای نمایش فهرست همهی فایلها در پوشهی خانگی خودمان:
[me@linuxbox ~]$ find ~
در بیشتر سیستمهای فعال، این دستور فهرست بسیار بلندی تولید میکند.
از آنجا که خروجی روی خروجی استاندارد (stdout) چاپ میشود، میتوانیم آن را با pipe (|) به دستورات دیگر بدهیم.
مثلاً برای شمارش تعداد کل فایلها:
[me@linuxbox ~]$ find ~ | wc -l
47068
یعنی حدود ۴۷ هزار فایل در پوشهی خانگی ما وجود دارد!
زیبایی find در این است که میتوانیم آن را طوری تنظیم کنیم تا فقط فایلهایی با ویژگیهای خاص را پیدا کند.
آزمونها (Tests)
فرض کنید میخواهیم فقط فهرستی از دایرکتوریها بهدست آوریم، نه فایلها.
در این حالت، میتوانیم از تست -type d استفاده کنیم:
[me@linuxbox ~]$ find ~ -type d | wc -l
1695
بهطور مشابه، اگر بخواهیم فقط فایلهای معمولی (regular files) را جستوجو کنیم:
[me@linuxbox ~]$ find ~ -type f | wc -l
38737
انواع تستهای نوع فایل در find
| حرف | نوع فایل | توضیح |
|---|---|---|
| b | block special | فایلهای دستگاه بلوکی (مثل دیسکها) |
| c | character special | فایلهای دستگاه کاراکتری (مثل ترمینالها) |
| d | directory | پوشه |
| f | regular file | فایل معمولی |
| l | symbolic link | لینک نمادین |
جستوجو بر اساس نام و اندازه
میتوانیم با افزودن چند تست دیگر، جستوجوی دقیقتری انجام دهیم.
برای مثال، فرض کنید میخواهیم تمام فایلهای معمولی با پسوند .JPG را که بزرگتر از ۱ مگابایت هستند، پیدا کنیم:
[me@linuxbox ~]$ find ~ -type f -name "*.JPG" -size +1M | wc -l
840
در این مثال:
-name "*.JPG"یعنی فقط فایلهایی که نامشان با الگوی دادهشده مطابقت دارد.
الگو را درون گیومه گذاشتهایم تا پوسته (shell) خودش آن را گسترش ندهد.-size +1Mیعنی فایلهایی بزرگتر از یک مگابایت.
علامتها در مقابل اندازه معنی خاصی دارند:
+یعنی بزرگتر از مقدار مشخصشده-یعنی کوچکتر از مقدار مشخصشده- بدون علامت یعنی دقیقاً برابر با آن مقدار
حرف پس از عدد واحد اندازه را تعیین میکند:
واحدهای اندازه در find
| کاراکتر | واحد اندازه | توضیح |
|---|---|---|
| b | بلوکهای ۵۱۲ بایتی | مقدار پیشفرض در صورت عدم ذکر واحد |
| c | بایت | |
| w | واژههای ۲ بایتی | |
| k | کیلوبایت (۱۰۲۴ بایت) | |
| M | مگابایت (۱٬۰۴۸٬۵۷۶ بایت) | |
| G | گیگابایت (۱٬۰۷۳٬۷۴۱٬۸۲۴ بایت) |
به این ترتیب، دستور find ابزار قدرتمندی است که میتواند بر اساس نوع، اندازه، نام، زمان تغییر، مجوزها و بسیاری ویژگیهای دیگر فایلها را پیدا کند —
و این تواناییها آن را به یکی از پرکاربردترین ابزارهای لینوکس تبدیل کرده است.
دستور find از مجموعهی بسیار گستردهای از تستها (Tests) پشتیبانی میکند.
در جدول زیر، پرکاربردترین آنها را مرور میکنیم.
در مواردی که ورودی عددی لازم است، همان نشانهگذاریهای + و - که قبلاً توضیح داده شد (بیشتر از / کمتر از / دقیقاً برابر) نیز قابل استفادهاند.
📋 جدول ۱۷-۳: تستهای متداول در find
| تست | توضیح |
|---|---|
| -cmin n | فایلها یا دایرکتوریهایی را پیدا میکند که محتوایشان یا ویژگیهایشان دقیقاً n دقیقه پیش تغییر کردهاند. برای کمتر از n دقیقه: -n، برای بیشتر از n دقیقه: +n. |
| -cnewer file | فایلها یا دایرکتوریهایی را پیدا میکند که جدیدتر از فایل مشخصشده هستند (از نظر زمان تغییر). |
| -ctime n | فایلها یا دایرکتوریهایی را پیدا میکند که محتوایشان یا ویژگیهایشان n×۲۴ ساعت پیش تغییر کرده است. |
| -empty | فایلها یا پوشههای خالی را پیدا میکند. |
| -group name | فایلها یا پوشههایی را که متعلق به گروه خاصی هستند پیدا میکند. گروه را میتوان با نام یا شناسه عددی گروه (GID) مشخص کرد. |
| -iname pattern | مثل -name است ولی به حروف بزرگ و کوچک حساس نیست. |
| -inum n | فایلهایی را با شمارهٔ inode مشخصشده پیدا میکند — برای یافتن تمام hard linkها به یک inode مفید است. |
| -mmin n | فایلها یا دایرکتوریهایی را پیدا میکند که محتوایشان n دقیقه پیش ویرایش شده است. |
| -mtime n | فایلها یا دایرکتوریهایی را پیدا میکند که محتوایشان n×۲۴ ساعت پیش تغییر کرده است. |
| -name pattern | فایلها یا پوشههایی را مییابد که با الگوی wildcard دادهشده تطابق دارند. |
| -newer file | فایلها یا پوشههایی را مییابد که جدیدتر از فایل مشخصشده هستند. این گزینه در اسکریپتهای پشتیبانگیری (backup) بسیار مفید است: میتوانید در هر بار بکاپ، تاریخ آخرین بکاپ را در فایلی ذخیره کنید و سپس با find -newer بفهمید کدام فایلها از آن زمان تغییر کردهاند. |
| -nouser | فایلها یا پوشههایی را پیدا میکند که هیچ مالک معتبری در سیستم ندارند — معمولاً متعلق به حسابهای حذفشده یا فعالیتهای مشکوک. |
| -nogroup | مشابه -nouser، ولی برای گروههایی که دیگر وجود ندارند. |
| -perm mode | فایلها یا پوشههایی با مجوز (permission) خاص را پیدا میکند. حالت دسترسی میتواند بهصورت عددی (مثلاً 755) یا نمادین (مثلاً u+rwx) نوشته شود. |
| -samefile name | مانند -inum است، ولی بهجای شماره inode، نام فایل مرجع داده میشود. فایلهایی را پیدا میکند که همان inode را با آن فایل بهاشتراک میگذارند. |
| -size n | فایلهایی را مییابد که اندازهشان برابر، بزرگتر یا کوچکتر از n است (بسته به وجود + یا -). |
| -type c | فایلهایی را مییابد که از نوع c هستند (مثلاً f برای فایل معمولی، d برای دایرکتوری، l برای لینک و غیره). |
| -user name | فایلها یا پوشههایی را پیدا میکند که متعلق به کاربر مشخصشده هستند. نام کاربر میتواند username یا شناسه عددی (UID) باشد. |
🔎 این فهرست کامل نیست — دستور man find جزئیات و تستهای پیشرفتهتری را ارائه میدهد،
از جمله جستوجو بر اساس زمان دسترسی (access time)، مجوزها، عمق دایرکتوری و ترکیب شرطها با عملگرهای منطقی مثل -and, -or, ! (not).
عملگرها (Operators)
با وجود تمام تستهایی که دستور find ارائه میدهد، گاهی لازم است بتوانیم روابط منطقی بین تستها را دقیقتر توصیف کنیم.
برای مثال، فرض کنید میخواهیم بررسی کنیم که آیا تمام فایلها و زیرپوشههای یک دایرکتوری دسترسی ایمن (permissions) مناسبی دارند یا نه.
در این حالت باید:
- تمام فایلهایی را پیدا کنیم که مجوزشان متفاوت از 0600 است، و
- تمام دایرکتوریهایی را که مجوزشان متفاوت از 0700 است.
خوشبختانه دستور find امکان ترکیب تستها با استفاده از عملگرهای منطقی را فراهم میکند تا روابط پیچیدهتر ایجاد کنیم.
مثلاً برای مثال بالا، میتوانیم بنویسیم:
[me@linuxbox ~]$ find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
در نگاه اول شاید عجیب به نظر برسد، اما وقتی مفهوم عملگرها را بدانیم، ساده است.
📘 جدول ۱۷-۴: عملگرهای منطقی find
| عملگر | توضیح |
|---|---|
| -and | تستها در دو طرف عملگر باید هر دو درست (true) باشند تا نتیجهٔ نهایی درست شود. میتوان به صورت کوتاهشده -a نیز نوشت. نکته: اگر هیچ عملگری بین تستها ننویسیم، -and بهصورت پیشفرض فرض میشود. |
| -or | اگر هر یک از تستها در دو طرف درست باشد، نتیجه درست میشود. میتوان به صورت کوتاهشده -o نیز نوشت. |
| -not | نتیجهٔ تست بعدی را معکوس میکند (یعنی اگر تست نادرست بود، درست میشود و برعکس). میتوان بهجای آن از ! استفاده کرد. |
| ( ) | تستها و عملگرها را برای تعیین ترتیب اجرا (precedence) گروهبندی میکند. بهصورت پیشفرض، find از چپ به راست ارزیابی میکند، اما میتوان با پرانتزها ترتیب را تغییر داد. حتی اگر لازم نباشد، گاهی برای خوانایی بهتر دستور مفید است. از آنجا که پرانتزها در پوسته معنای خاصی دارند، باید با بکاسلش \ از آنها محافظت کرد تا به find منتقل شوند. |
تحلیل دستور مثال بالا
اگر ساختار کلی دستور را نگاه کنیم، دو بخش داریم که با عملگر -or از هم جدا شدهاند:
( expression 1 ) -or ( expression 2 )
این منطقی است، چون داریم دو نوع بررسی انجام میدهیم:
۱. فایلهایی که دسترسی مناسبی ندارند.
۲. پوشههایی که دسترسی مناسبی ندارند.
چرا از -or استفاده کردهایم و نه -and؟
چون هر فایل فقط میتواند یکی از این دو حالت را داشته باشد — یا فایل است یا دایرکتوری، نه هر دو.
پس باید بگوییم:
( فایل با مجوز اشتباه ) -or ( پوشه با مجوز اشتباه )
تعریف «مجوز اشتباه»
ما بهطور مستقیم نمیتوانیم «مجوز بد» را تست کنیم،
اما میتوانیم بگوییم «مجوز غیر از خوب»، چون میدانیم مجوز خوب چیست.
برای فایلها، مجوز خوب 0600 است، و برای دایرکتوریها 0700.
پس تستهایمان میشوند:
-type f -and -not -perm 0600
-type d -and -not -perm 0700
و چون -and بهصورت پیشفرض وجود دارد، میتوانیم آن را حذف کنیم.
اگر همه را با هم ترکیب کنیم، دستور نهایی چنین است:
find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
و همانطور که گفته شد، پرانتزها باید با بکاسلش (\) محافظت شوند تا پوسته آنها را تفسیر نکند.
منطق اجرای عملگرها
فرض کنید دو عبارت داریم که با یک عملگر منطقی از هم جدا شدهاند:
expr1 -operator expr2
در همهٔ حالتها، عبارت expr1 همیشه اجرا میشود،
اما اجرای expr2 بستگی به نتیجهٔ expr1 و نوع عملگر دارد.
جدول ۱۷-۵: منطق AND/OR در find
| نتیجهٔ expr1 | عملگر | وضعیت اجرای expr2 |
|---|---|---|
| True | -and | همیشه اجرا میشود |
| False | -and | اجرا نمیشود |
| True | -or | اجرا نمیشود |
| False | -or | همیشه اجرا میشود |
چرا این رفتار مهم است؟
هدف از این منطق، بهینهسازی عملکرد است.
مثلاً در عبارت:
expr1 -and expr2
اگر expr1 نتیجهای نادرست (false) بدهد، دیگر نیازی نیست expr2 بررسی شود، چون نتیجهٔ کلی در هر صورت نادرست خواهد بود.
بهطور مشابه، در عبارت:
expr1 -or expr2
اگر expr1 درست باشد، دیگر نیازی به اجرای expr2 نیست، چون نتیجهٔ کلی حتماً درست است.
این رفتار علاوه بر افزایش سرعت، در کنترل ترتیب اجرای اعمال (actions) نیز نقش دارد —
که در ادامهٔ فصل هنگام بررسی عملگرهای عملیاتی (-exec, -delete, و غیره) اهمیت آن را خواهیم دید.
اعمال از پیش تعریفشده (Predefined Actions)
تا اینجا یاد گرفتیم که دستور find میتواند فهرستی از فایلها را بر اساس معیارهای مختلف نمایش دهد.
اما واقعاً هدف نهایی فقط دیدن فهرست نیست — بلکه انجام کاری روی آن فایلهاست.
خوشبختانه find اجازه میدهد اعمال (Actions) خاصی را روی نتایج جستوجو انجام دهیم.
تعدادی از این اعمال از پیش تعریفشدهاند و در کنار آنها میتوان اعمال دلخواه (User-defined) هم نوشت.
ابتدا نگاهی به چند عمل پرکاربرد بیندازیم:
📘 جدول ۱۷-۶: اعمال از پیش تعریفشده در find
| عمل (Action) | توضیح |
|---|---|
| -delete | فایل یا دایرکتوریای که با معیارها مطابقت دارد را حذف میکند. |
| -ls | معادل اجرای دستور ls -dils برای هر فایل مطابقتیافته است؛ خروجی در ترمینال چاپ میشود. |
| مسیر کامل فایل مطابقتیافته را روی خروجی استاندارد (stdout) چاپ میکند. این عمل بهصورت پیشفرض اجرا میشود اگر هیچ عمل دیگری مشخص نشده باشد. |
|
| -quit | بلافاصله پس از یافتن اولین نتیجه، جستوجو را متوقف میکند. |
همانند تستها، اعمال دیگری نیز وجود دارند؛ برای فهرست کامل باید صفحهی راهنما را بخوانید:
man find
در اولین مثال این فصل، فقط نوشتیم:
find ~
این دستور فهرستی از تمام فایلها و زیرپوشههای داخل پوشهی خانگی ما تولید کرد.
علت آن این است که اگر هیچ عملی مشخص نشود، عمل -print بهطور ضمنی اجرا میشود.
بنابراین دستور بالا معادل است با:
find ~ -print
حذف فایلها با find
میتوانیم find را طوری تنظیم کنیم که فایلهایی را حذف کند که معیار خاصی دارند.
برای مثال، حذف تمام فایلهایی که پسوند .BAK دارند (که معمولاً برای نسخههای پشتیبان استفاده میشود):
find ~ -type f -name '*.BAK' -delete
این دستور تمام فایلهای با پسوند .BAK را در پوشهی خانگی و زیرپوشههایش جستوجو کرده و در صورت یافتن، حذف میکند.
⚠️ هشدار بسیار مهم:
همیشه پیش از اجرای دستور-delete، ابتدا همان دستور را باfind ~ -type f -name '*.BAK' -printوقتی خروجی را بررسی و تأیید کردید، سپس
-deleteجایگزین کنید.
تأثیر عملگرهای منطقی بر اجرای اعمال
به دستور زیر نگاه کنید:
find ~ -type f -name '*.BAK' -print
این دستور تمام فایلهای معمولی (-type f) با نامی که به .BAK ختم میشود (-name '*.BAK') را یافته و مسیرشان را چاپ میکند (-print).
اما دلیل اینکه این دستور دقیقاً به این شکل کار میکند، به روابط منطقی بین تستها و اعمال برمیگردد.
بهصورت پیشفرض، بین هر تست و عمل در find یک رابطهی -and ضمنی وجود دارد.
میتوانیم دستور بالا را به شکل واضحتر بنویسیم:
find ~ -type f -and -name '*.BAK' -and -print
نحوهٔ اجرای گامبهگام دستور بالا
| تست / عمل | چه زمانی اجرا میشود |
|---|---|
| -type f | همیشه اجرا میشود (اولین تست در زنجیرهی -and) |
| -name '*.BAK' | فقط اگر نتیجهی -type f درست باشد |
فقط اگر هر دو تست قبلی (-type f و -name '*.BAK') درست باشند |
به همین دلیل ترتیب تستها و اعمال اهمیت دارد،
چون هر مرحله فقط در صورتی اجرا میشود که شرطهای قبل از آن نتیجهی صحیح داشته باشند.
اگر ترتیب را تغییر دهیم و عمل -print را اول بنویسیم، رفتار دستور کاملاً متفاوت میشود:
find ~ -print -and -type f -and -name '*.BAK'
در این حالت:
- ابتدا همهٔ فایلها چاپ میشوند (چون
-printهمیشه مقدار true برمیگرداند)، - سپس برای هر فایل چاپشده، تست نوع و نام انجام میشود — اما دیگر محدودیتی در خروجی نداریم.
نتیجه: این نسخه همهٔ فایلها را چاپ میکند، نه فقط فایلهای با پسوند .BAK.
🔹 جمعبندی:
رفتار نهایی دستور find وابسته به ترتیب تستها و اعمال و روابط منطقی بین آنهاست.
با درک این منطق میتوانیم دستورات پیچیده و دقیق بنویسیم — مثلاً حذف مشروط، جستوجوی چندمعیاره، یا پردازش گروهی فایلها.
اعمال کاربرساز (User-Defined Actions)
علاوه بر اعمال از پیش تعریفشده، میتوانیم هر دستور دلخواهی را هم روی فایلهای پیداشده اجرا کنیم.
روش سنتی این کار استفاده از عمل -exec است.
📘 ساختار دستور -exec
-exec command {} ;
- command → دستور مورد نظر شما
- {} → نمادی است که در هر بار اجرا با مسیر فایل فعلی جایگزین میشود
- ; → پایان دستور است و وجود آن اجباری است
مثلاً برای اجرای عملی مشابه -delete با استفاده از rm مینویسیم:
-exec rm '{}' ';'
چون آکولاد
{}و نقطهویرگول;در پوسته معناهای خاصی دارند،
باید آنها را در کوتیشن قرار دهید یا با بکاسلش (\) از تفسیرشان جلوگیری کنید.
اجرای تعاملی (با تأیید کاربر)
میتوانیم با استفاده از -ok به جای -exec، اجرای دستور را بهصورت تعاملی انجام دهیم.
در این حالت، برای هر فایل قبل از اجرا از کاربر تأیید گرفته میشود:
find ~ -type f -name 'foo*' -ok ls -l '{}' ';'
نمونهٔ خروجی:
< ls ... /home/me/bin/foo > ? y
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
< ls ... /home/me/foo.txt > ? y
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt
در این مثال، find فایلهایی را که با «foo» شروع میشوند پیدا کرده
و قبل از اجرای دستور ls -l برای هر فایل، از ما تأیید میخواهد.
بهبود کارایی اجرای دستورات
بهصورت پیشفرض، وقتی از -exec استفاده میکنیم،
find برای هر فایل تطبیقیافته یکبار دستور را اجرا میکند.
مثلاً اگر دو فایل پیدا شود، دو بار دستور ls اجرا میشود:
ls -l file1
ls -l file2
اما گاهی بهتر است تمام نتایج در یک دستور تجمیع شوند:
ls -l file1 file2
به این ترتیب فقط یک بار دستور اجرا میشود و کارایی بالاتر میرود.
🔸 روش اول: استفاده از قابلیت جدید find
برای این کار کافی است علامت پایانی ; را با + جایگزین کنید.
در این صورت find همهی مسیرها را در یک لیست واحد جمع کرده و
در یک اجرای واحد به دستور منتقل میکند.
مثلاً:
find ~ -type f -name 'foo*' -exec ls -l '{}' ';'
این نسخه ls را برای هر فایل جداگانه اجرا میکند.
اما اگر بنویسیم:
find ~ -type f -name 'foo*' -exec ls -l '{}' +
خروجی همان است،
اما دستور ls فقط یکبار اجرا میشود و تمام فایلها را با هم میگیرد.
🔸 روش دوم: استفاده از xargs
ابزار xargs ورودی را از stdin (مثل خروجی دستور قبل از خودش) میگیرد
و آن را بهصورت فهرست آرگومانها به دستور بعدی منتقل میکند.
برای مثال:
find ~ -type f -name 'foo*' -print | xargs ls -l
در اینجا خروجی find (لیست فایلها) به xargs داده میشود،
و xargs آنها را در قالب یک خط فرمان به دستور ls اضافه و اجرا میکند.
خروجی مشابه است:
-rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo
-rw-r--r-- 1 me me 0 2008-09-19 12:53 /home/me/foo.txt
نکتهٔ فنی: محدودیت طول خط فرمان
هرچند سیستمعامل اجازه میدهد تعداد زیادی آرگومان در یک خط فرمان قرار گیرد،
ولی این مقدار بینهایت نیست.
اگر تعداد آرگومانها بیش از حد زیاد شود، xargs:
- دستور را با حداکثر تعداد مجاز آرگومانها اجرا میکند،
- سپس این فرایند را تکرار میکند تا تمام ورودیها پردازش شوند.
برای دیدن حداکثر اندازهٔ مجاز خط فرمان در سیستم خودتان، میتوانید بنویسید:
xargs --show-limits
✅ خلاصهٔ کاربردها:
-exec command '{}' ';'→ اجرای دستور برای هر فایل جداگانه-exec command '{}' +→ اجرای یکباره دستور برای تمام فایلها-ok command '{}' ';'→ اجرای تعاملی با تأیید کاربر| xargs command→ ساخت آرگومانها از ورودی و اجرای دستور تنها در صورت نیاز
کار با نام فایلهای عجیب (Dealing With Funny Filenames)
در سیستمهای شبهیونیکس، نام فایلها میتوانند شامل فاصله (space) یا حتی کاراکتر newline (خط جدید) باشند.
این موضوع میتواند برای ابزارهایی مثل xargs مشکلساز شود؛
چون وقتی نام فایلی شامل فاصله باشد، xargs آن فاصله را بهعنوان جداکنندهی آرگومانها در نظر میگیرد،
و دستور نهایی هر بخش از نام را بهصورت آرگومان جداگانه تفسیر میکند.
برای رفع این مشکل، دستورهای find و xargs راهی ارائه دادهاند:
میتوان از کاراکتر null (NUL) بهجای فاصله بهعنوان جداکننده استفاده کرد.
در جدول ASCII، کاراکتر null با عدد صفر (0) نمایش داده میشود،
در حالی که کاراکتر فاصله با عدد ۳۲ مشخص است.
در دستور find، عمل -print0 باعث میشود خروجی فایلها با کاراکتر null جدا شود،
و در xargs، گزینهی --null (یا -0) به آن میگوید که ورودی را با همین فرمت بخواند.
نمونه:
find ~ -iname '*.jpg' -print0 | xargs --null ls -l
با استفاده از این روش، مطمئن میشویم که حتی فایلهایی با نامهایی شامل فاصله یا کاراکترهای غیرعادی نیز بهدرستی پردازش میشوند.
بازگشت به محیط تمرین (A Return To The Playground)
حالا وقت آن است که همهی چیزهایی را که یاد گرفتهایم در عمل امتحان کنیم.
یک محیط آزمایشی میسازیم تا بتوانیم دستورات find را روی آن تست کنیم.
ابتدا با دو دستور ساده، مجموعهای از پوشهها و فایلها میسازیم:
[me@linuxbox ~]$ mkdir -p playground/dir-{001..100}
[me@linuxbox ~]$ touch playground/dir-{001..100}/file-{A..Z}
در عرض چند ثانیه، ۱۰۰ پوشه ساخته میشود که هرکدام شامل ۲۶ فایل خالی هستند!
این همان قدرت خط فرمان لینوکس است — سعی کنید همین کار را با رابط گرافیکی انجام دهید! 😄
توضیح ساختار
در اینجا از چند قابلیت پوسته استفاده کردیم:
mkdir -p→ مسیرهای تودرتو را در صورت لزوم میسازد.- گسترش آکلادی (Brace Expansion) →
{001..100}و{A..Z}باعث تولید بازهای از مقادیر میشود. touch→ زمان آخرین دسترسی و تغییر فایل را تنظیم میکند. اگر فایلی وجود نداشته باشد، یک فایل خالی ایجاد میکند.
جستوجو در Playground
در محیط تمرین خود ۱۰۰ فایل به نام file-A ساختهایم.
بیایید همهی آنها را پیدا کنیم:
[me@linuxbox ~]$ find playground -type f -name 'file-A'
برخلاف ls، دستور find فایلها را به ترتیب مرتبشده نمایش نمیدهد،
بلکه بر اساس چیدمان فیزیکی فایلها در دیسک لیست را برمیگرداند.
میتوانیم مطمئن شویم که واقعاً ۱۰۰ فایل پیدا شدهاند:
[me@linuxbox ~]$ find playground -type f -name 'file-A' | wc -l
100
جستوجوی فایلها بر اساس زمان تغییر (Modification Time)
این ویژگی هنگام پشتیبانگیری (backup) یا مرتبسازی زمانی فایلها بسیار مفید است.
ابتدا یک فایل مرجع میسازیم که زمان فعلی را ذخیره کند:
[me@linuxbox ~]$ touch playground/timestamp
این دستور یک فایل خالی با نام timestamp میسازد و زمان آخرین تغییر آن را به زمان فعلی سیستم تنظیم میکند.
برای مشاهدهی جزئیات کامل این فایل از دستور stat استفاده میکنیم —
دستوری قدرتمندتر از ls که همهی ویژگیهای فایل را نمایش میدهد:
[me@linuxbox ~]$ stat playground/timestamp
نمونهی خروجی:
File: `playground/timestamp'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 803h/2051d Inode: 14265061 Links: 1
Access: (0644/-rw-r--r--) Uid: (1001/me) Gid: (1001/me)
Access: 2008-10-08 15:15:39.000000000 -0400
Modify: 2008-10-08 15:15:39.000000000 -0400
Change: 2008-10-08 15:15:39.000000000 -0400
اگر دستور touch را دوباره اجرا کنیم، میبینیم که زمانها بهروزرسانی میشوند:
[me@linuxbox ~]$ touch playground/timestamp
[me@linuxbox ~]$ stat playground/timestamp
بهروزرسانی فایلها با find
حالا میخواهیم همهی فایلهای file-B را در Playground بهروزرسانی کنیم (یعنی زمان تغییرشان را به زمان فعلی ببریم):
[me@linuxbox ~]$ find playground -type f -name 'file-B' -exec touch '{}' ';'
این دستور همهی فایلهای file-B را پیدا کرده و با دستور touch بهروزرسانی میکند.
حالا برای پیدا کردن فایلهایی که جدیدتر از فایل مرجع timestamp هستند:
[me@linuxbox ~]$ find playground -type f -newer playground/timestamp
نتیجه شامل تمام ۱۰۰ فایل file-B خواهد بود،
چون همهی آنها بعد از ساخت timestamp بهروزرسانی شدند،
و بنابراین طبق تست -newer، جدیدتر از آن فایل محسوب میشوند.
آزمایش دوبارهی مجوزهای اشتباه
در پایان، همان تستی که قبلاً برای مجوزهای نادرست (bad permissions) داشتیم را روی محیط Playground اجرا میکنیم:
[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
این دستور همهی فایلها و پوشههایی را پیدا میکند که مجوزشان با حالت ایمن مورد نظر (۰۶۰۰ برای فایلها، ۰۷۰۰ برای دایرکتوریها) مطابقت ندارد.
🔹 به این ترتیب، با همین چند دستور یاد گرفتیم چطور find را بهصورت حرفهای برای مدیریت، بررسی، و پردازش فایلها در سیستم لینوکس بهکار بگیریم.
ادامهٔ آزمایش در Playground
وقتی دستور زیر را اجرا میکنیم:
find playground \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
خروجی شامل تمام ۱۰۰ دایرکتوری و ۲۶۰۰ فایل در مسیر playground میشود
(بهعلاوهٔ خود پوشهٔ playground و فایل timestamp، یعنی جمعاً ۲۷۰۲ مورد)
چون هیچکدام از آنها مطابق تعریف ما از «مجوز ایمن» نیستند.
اعمال مجوزهای جدید با استفاده از find
با استفاده از عملگرها (operators) و اعمال (actions) حالا میتوانیم دستور را طوری تغییر دهیم که مجوز فایلها و پوشهها را اصلاح کند:
[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 -exec chmod 0600 '{}' ';' \) -or \( -type d -not -perm 0700 -exec chmod 0700 '{}' ';' \)
در این دستور:
- فایلهایی که مجوزشان متفاوت از
0600است، باchmod 0600اصلاح میشوند. - دایرکتوریهایی که مجوزشان متفاوت از
0700است، باchmod 0700تنظیم میشوند.
در کارهای روزمره، معمولاً اجرای دو دستور جداگانه (یکی برای فایلها و یکی برای پوشهها) سادهتر است، مثل این:
find playground -type f -not -perm 0600 -exec chmod 0600 '{}' ';'
find playground -type d -not -perm 0700 -exec chmod 0700 '{}' ';'
اما این مثال ترکیبی بهخوبی نشان میدهد که چگونه میتوانیم تستها، عملگرها و اعمال را با هم ترکیب کنیم تا عملیات پیچیده و هدفمند انجام دهیم.
گزینهها (Options)
گزینهها در دستور find برای کنترل دامنهٔ جستوجو (scope) استفاده میشوند.
میتوان آنها را همراه با تستها و اعمال در یک دستور ترکیب کرد تا رفتار find دقیقتر شود.
در جدول زیر، متداولترین گزینهها آورده شده است:
📘 جدول ۱۷-۷: گزینههای پرکاربرد در find
| گزینه | توضیح |
|---|---|
| -depth | باعث میشود find ابتدا محتوای هر دایرکتوری را بررسی کند و در انتها خود دایرکتوری را پردازش کند. این گزینه بهطور خودکار زمانی فعال میشود که از عمل -delete استفاده کنید. |
| -maxdepth levels | بیشترین تعداد سطحهایی را که find در درخت دایرکتوری پایین میرود مشخص میکند. مثلاً -maxdepth 1 فقط دایرکتوری جاری را بررسی میکند و وارد زیرپوشهها نمیشود. |
| -mindepth levels | حداقل سطحی را مشخص میکند که از آن به بعد تستها و اعمال اجرا شوند. مثلاً -mindepth 1 باعث میشود خود دایرکتوری اصلی بررسی نشود و فقط زیرپوشهها پردازش شوند. |
| -mount | به find دستور میدهد که وارد دایرکتوریهایی که روی سیستم فایلهای دیگر (mount points) قرار دارند، نشود. |
| -noleaf | به find میگوید که فرض بهینهسازی بر اساس ساختار فایلسیستم یونیکسی را نادیده بگیرد. در زمان جستوجو در سیستمفایلهای DOS/Windows یا CD-ROM باید از این گزینه استفاده شود. |
جمعبندی
میتوان گفت که دستور locate به همان اندازه ساده است که find پیچیده است!
هر دو ابزار مفیدند، اما کاربردشان متفاوت است.
- locate برای جستوجوی سریع بر اساس نام فایلها عالی است.
- find برای جستوجوی دقیق بر اساس ویژگیها، زمانها، دسترسیها و انجام اعمال خودکار بیرقیب است.
اگر مرتب با فایلها و ساختار سیستم لینوکس کار میکنید،
یادگیری و تمرین دستور find بهمرور باعث میشود درک عمیقتری از رفتار فایلسیستم لینوکس پیدا کنید.
مطالعهٔ بیشتر
- برنامههای
locate,updatedb,find, وxargsهمگی بخشی از بستهی GNU findutils هستند.
برای مطالعهٔ مستندات رسمی و جامع این ابزارها (بهویژه در محیطهای امنیتی حساس)، به سایت رسمی GNU مراجعه کنید:
🔗 https://www.gnu.org/software/findutils/