فرمانهایی برای کاربران پیشرفتهتر
-exec COMMAND \;
COMMAND را روی هر فایلی که find تطبیق بدهد، اجرا میکند. رشته فرمان با کاراکتر ; خاتمه مییابد (کاراکتر «;» برای مشخص نمودن آن که پوسته بدون تفسیر آن به عنوان یک کاراکتر خاص، به طور لفظی آنرا به find عبور بدهد، escape میشود).
bash$ find ~/ -name '*.txt' /home/bozo/.kde/share/apps/karm/karmdata.txt /home/bozo/misc/irmeyc.txt /home/bozo/test-scripts/1.txt
اگر COMMAND شامل {} باشد، آنوقت find نام مسیر کامل فایل انتخاب شده را به جای "{}" جایگزین میکند.
find ~/ -name 'core*' -exec rm {} \; #تمام فایلهای روگرفت حافظه(core) را از دایرکتوری خانه کاربر حذف میکند.
find /home/bozo/projects -mtime -1 # #تمام فایلهای درخت دایرکتوری /home/bozo/projects را که در مدت #+روز گذشته (روز جاری منهای یک) ویرایش گردیدهاند، لیست میکند. # find /home/bozo/projects -mtime 1 # # #mtime = زمان آخرین ویرایش فایل مقصد #ctime = زمان آخرین تغییر وضعیت ( از طریق chmod یا غیر از آن) #atime = زمان آخرین دستیابی DIR=/home/bozo/junk_files find "$DIR" -type f -atime +5 -exec rm {} \; # #ابروها جای نگهدارنده جهت نام مسیر خروجی توسط find هستند. # #تمام فایلها در /home/bozo/junk_files را که ظرف «حداقل» #+5 روز (علامت بعلاوه در +5) دستیابی نگردیدهاند، حذف میکند. # #که در "-type filetype" # # # # #(صفحه man و info فرمان find دارای فهرست کامل گزینهها هستند.)
find /etc -exec grep '[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*' {} \; #آدرسهای IP (xxx.xxx.xxx.xxx) را در فایلهای دایرکتوری /etc جستجو میکند. #موارد نامربوطی هم یافت میشوند. آیا میتوان آنها را فیلتر نمود؟ #شاید به وسیله: find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \ | grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$' # #[:digit:] یکی از کلاسهای کاراکتر مطرح شده با استاندارد POSIX 1003.2 است #با تشکر از Stéphane Chazelas
گزینه -exec برای فرمان find نباید با exec توکار پوسته اشتباه بشود. |
مثال 16-3. Badname، از قلم انداختن نامفایلهای شامل کاراکترهای نامناسب و فضای سفید در دایرکتوری جاری.
#!/bin/bash # #حذف نام فایلهای دایرکتوری جاری که شامل کاراکترهای نامناسب هستند. for filename in * do badname=`echo "$filename" | sed -n /[\+\{\;\"\\\=\?~\(\)\<\>\&\*\|\$]/p` #badname=`echo "$filename" | sed -n '/[+{;"\=?~()<>&*|$]/p'` نیز کار میکند. #فایلهای شامل کاراکترهای نامطبوع + { ; " \ = ? ~ ( ) < > & * | $ را حذف میکند. # rm $badname 2>/dev/null # done #اکنون، پرداختن به نام فایلهای شامل هر گونه فضای سفید. find . -name "* *" -exec rm -f {} \; #نام مسیر فایل که فرمان find پیدا میکند جانشین {} میگردد. #کاراکتر \ اطمینان ایجاد میکند که کاراکتر ; به طور لفظی و #به عنوان انتهای فرمان تفسیر میگردد. exit 0 # #فرمانهای زیر این سطر به علت فرمان exit اجرا نخواهند گردید. #جایگزینی برای اسکریپت بالا: find . -name '*[+{;"\\=?~()<>&*|$ ]*' -maxdepth 0 \ -exec rm -f '{}' \; #گزینه -maxdepth 0 اطمینان ایجاد میکند که find #+دایرکتوریهای فرعی در $PWD را جستجو نخواهد نمود. #(با تشکر از S.C.)
مثال 16-4. حذف یک فایل از طریق شماره inode آن
#!/bin/bash # # #+از قبیل ? یا - شروع بشود. ARGCOUNT=1 # E_WRONGARGS=70 E_FILE_NOT_EXIST=71 E_CHANGED_MIND=72 if [ $# -ne "$ARGCOUNT" ] then echo "Usage: `basename $0` filename" exit $E_WRONGARGS fi if [ ! -e "$1" ] then echo "File \""$1"\" does not exist." exit $E_FILE_NOT_EXIST fi inum=`ls -i | grep "$1" | awk '{print $1}'` # # #هر فایل دارای یک inode است، رکوردی که اطلاعات آدرس فیزیکی را نگهداری میکند. # echo; echo -n "Are you absolutely sure you want to delete \"$1\" (y/n)? " #گزینه -i با فرمان rm نیز این پرسش را مطرح میکند. read answer case "$answer" in [nN]) echo "Changed your mind, huh?" exit $E_CHANGED_MIND ;; *) echo "Deleting file \"$1\".";; esac find . -inum $inum -exec rm {} \; # # #+متن خروجی فرمان find هستند. echo "File "\"$1"\" deleted!" exit 0
فرمان find بدون گزینه -exec نیز کار میکند.
#!/bin/bash #یافتن فایلهای suid کاربر ارشد. #یک فایل suid ناشناس میتواند بیانگر یک حفره امنیتی، یا حتی نفوذ به سیستم باشد. directory="/usr/sbin" #همچنین میتوان /sbin، /bin، /usr/bin، /usr/local/bin، وغیره را امتحان کرد. permissions="+4000" #suid root (خطرناک!) for file in $( find "$directory" -perm "$permissions" ) do ls -ltF --author "$file" done
برای اسکریپتهایی که از find استفاده میکنند، مثال 16-30، مثال 3-4، و مثال 11-10 مشاهده نمایید. صفحه man آن، جزییات بیشتری در مورد این فرمان قدرتمند و پیچیده ارایه میکند.
فیلتری برای تغذیه شناسهها به یک فرمان، و همچنین ابزاری برای یکپارچه کردن خود فرمانها است. این فرمان جریان دادهها را به تکههای به اندازه کافی کوچک جهت پردازش توسط فیلترها و فرمانها تجزیه میکند. آن را به مثابه جایگزین قدرتمندی برای backquoteها در نظر بگیرید. در وضعیتهایی که در آنها جایگزینی فرمان با یک خطای «too many arguments» (شناسههای بسیار زیاد) ناموفق میگردد، اغلب جایگزین کردن xargs کار میکند. [1] به طور معمول، xargs از stdin یا از یک لوله میخواند، اما میتواند شناسههایش را از یک فایل تعیین شده نیز بخواند.
فرمان پیشفرض برای xargs فرمان echo است. این به معنای آن است که ورودی لولهکشی شده به xargs ممکن است دارای کاراکترهای تعویض سطر و سایر کاراکترهای فضای سفیدی باشد که حذف بشوند.
bash$ ls -l total 0 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file1 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file2 bash$ ls -l | xargs total 0 -rw-rw-r-- 1 bozo bozo 0 Jan 29 23:58 file1 -rw-rw-r-- 1 bozo bozo 0 Jan... bash$ find ~/mail -type f | xargs grep "Linux" ./misc:User-Agent: slrn/0.9.8.1 (Linux) ./sent-mail-jul-2005: hosted by the Linux Documentation Project. ./sent-mail-jul-2005: (Linux Documentation Project Site, rtf version) ./sent-mail-jul-2005: Subject: Criticism of Bozo's Windows/Linux article ./sent-mail-jul-2005: while mentioning that the Linux ext2/ext3 filesystem . . .
ls | xargs -p -l gzip همه فایلهای دایرکتوری جاری را، در هر نوبت یک فایل و با یک اعلان قبل از هر عمل gzip میکند.
توجه نمایید که xargs شناسههای تحویل شده به آن را به صورت ترتیبی ، یکی در هر نوبت، پردازش میکند.
bash$ find /usr/bin | xargs file /usr/bin: directory /usr/bin/foomatic-ppd-options: perl script text executable . . . |
یک گزینه جالب توجه xargs گزینه -n NN است، که تعداد شناسههای عبور داده شده را به NN محدود میکند. ls | xargs -n 8 echo فایلهای دایرکتوری جاری را در 8 ستون فهرست میکند. |
یک گزینه سودمند دیگر -0، در ترکیب با find -print0 یا grep -lZ است. این گزینه، مدیریت شناسههای شامل فضای سفید یا نقلقولها را ممکن میسازد. find / -type f -print0 | xargs -0 grep -liwZ GUI | xargs -0 rm -f grep -rliwZ GUI / | xargs -0 rm -f هر کدام از موارد فوق هر فایل شامل GUI را حذف خواهد نمود. (با تشکر از S.C.) یا: cat /proc/"$pid"/"$OPTION" | xargs -0 echo # # |
گزینه -P با xargs اجرای پردازشها به طور موازی را مجاز میکند. این کار سرعت اجرا در یک ماشین با CPU چند هستهای را افزایش میدهد. #!/bin/bash ls *gif | xargs -t -n1 -P2 gif2png # |
مثال 16-5. Logfile: کاربرد xargs برای دیدهبانی log سیستم
#!/bin/bash #با بریدن از انتهای فایل /var/log/messages یک # # #فایل /var/log/messages باید قابل خواندن همگانی باشد. # LINES=5 ( date; uname -a ) >>logfile # echo ---------------------------------------------------------- >>logfile tail -n $LINES /var/log/messages | xargs | fmt -s >>logfile echo >>logfile echo >>logfile exit 0 #توجه: # #به طوریکه Frank Wang توضیح میدهد: #+ #+منبع میتوانند برای xargs ناهنجاری ایجاد نمایند. # #او پیشنهاد میدهد سطر زیر جایگزین سطر 15 گردد: # #تمرین #-------- #این اسکریپت را برای پیگردی تغییرات /var/log/messages #+در فاصلههای زمانی 20 دقیقهای، ویرایش کنید. #اشاره: از فرمان "watch" استفاده کنید.
همچون در find، یک جفت براکت کمانی به عنوان جاینگهدارندهای برای تعویض متن به کار میرود.
مثال 16-6. کپی کردن فایلهای دایرکتوری جاری به یک دایرکتوری دیگر
#!/bin/bash # #کپی (تفصیلی) تمام فایلها از دایرکتوری جاری ($PWD) #+به دایرکتوری تعیین شده در خط فرمان. E_NOARGS=85 if [ -z "$1" ] #خروج در صورتیکه شناسهای ارایه نشده باشد. then echo "Usage: `basename $0` directory-to-copy-to" exit $E_NOARGS fi ls . | xargs -i -t cp ./{} $1 # #-t گزینه «مطول» (برونداد خط فرمان در stderr). #-i گزینه «تعویض رشتهها». #{} یک جاینگهدارنده برای متن خروجی. #این مشابه استفاده از جفت براکتهای کمانی در find است. # #لیست فایلها در دایرکتوری جاری یعنی (ls .)، خروجی ls را به #+عنوان شناسهها به xargs ( با گزینههای -i -t ) عبور میدهد #+سپس این شناسهها {} به دایرکتوری جدید $1 کپی (cp) میشود. # #پیامد کلی آن معادل دقیق cp * $1 است، مگر اینکه فایلهایی #+دارای کاراکترهای «فضای سفید» تعبیه شده در نامشان باشند. exit 0
مثال 16-7. کشتن پردازشها بواسطه نام
#!/bin/bash # #این اسکریپت را با kill-process.sh مقایسه کنید. #برای نمونه، ./kill-byname.sh xterm را امتحان کنید و #+نگاه کنید، تمام xtermها روی میزکار شما ناپدید میشوند. #هشدار: #--------------------------------- #این یک اسکریپت نسبتاً خطرناک است. #اجرای آن از روی بیدقتی (مخصوصاً به عنوان root) میتواند #+باعث از دست رفتن دادهها و سایر اثرات ناخواسته بشود. E_BADARGS=66 if test -z "$1" #آیا شناسهای در خط فرمان ارایه نشده؟ then echo "Usage: `basename $0` Process(es)_to_kill" exit $E_BADARGS fi PROCESS_NAME="$1" ps ax | grep "$PROCESS_NAME" | awk '{print $1}' | xargs -i kill {} 2&>/dev/null # # #نکتهها: #-i گزینه «تعویض رشتهها» برای xargs است. #ابروها جاینگهدارنده برای جایگزینی هستند. #2&>/dev/null مانع پیغام خطاهای ناخواسته میشود. # #grep "$PROCESS_NAME" میتواند با pidof "$PROCESS_NAME" تعویض شود؟ # exit $? #فرمان killall دارای اثری مانند این اسکریپت است #+اما، به کار بردن آن خیلی آموزشی نیست.
مثال 16-8. تجزیه و تحلیل تکرار کلمه با استفاده از xargs
#!/bin/bash # #جهت تجزیه سطرهای متن به کلمههای منفرد xargs را به کار میبرد. #این مثال را با اسکریپت wf.sh در مثال 16-12 مقایسه کنید. # ARGS=1 E_BADARGS=85 E_NOFILE=86 if [ $# -ne "$ARGS" ] # then echo "Usage: `basename $0` filename" exit $E_BADARGS fi if [ ! -f "$1" ] # then echo "File \"$1\" does not exist." exit $E_NOFILE fi # cat "$1" | xargs -n1 | \ # tr A-Z a-z | \ # sed -e 's/\.//g' -e 's/\,//g' -e 's/ /\ /g' | \ # #+ sort | uniq -c | sort -nr # #+ # #این همان کار مثال wf.sh را انجام میدهد، اما کمی #+سنگینتر، و به طور کندتری اجرا میگردد (چرا؟). exit $?
ارزشیابی کننده عبارت همه منظوره: شناسهها را مطابق عملیات تعیین شده بهم پیوست نموده و ارزیابی میکند (شناسهها باید با فاصله جدا شده باشند). عملیات ممکن است محاسباتی، مقایسهای، رشتهای، یا منطقی باشند.
8 برگشت میدهد
2 برگشت میدهد
پیغام خطای expr: division by zero برگشت میدهد
عملیات حسابی غیر مجاز پذیرفته نیست.
15 برگشت میدهد
عملگر ضرب موقع استفاده در یک عبارت حسابی با expr بایستی escape گردد.
افزایش یک متغیر، با همان نتیجه let y=y+1 و y=$(($y+1)). این یک مثال از عبارت حسابی است.
رشته فرعی به طول $length کاراکتر را با شروع از محل $position استخراج میکند.
#!/bin/bash #نمایش برخی از کاربردهای expr # echo # # echo "Arithmetic Operators" echo a=`expr 5 + 3` echo "5 + 3 = $a" a=`expr $a + 1` echo echo "a + 1 = $a" echo "(incrementing a variable)" a=`expr 5 % 3` #عملگر modulo (باقیمانده تقسیم صحیح) echo echo "5 mod 3 = $a" echo echo # # #اگر صحیح باشد 1، اگر غلط باشد 0 برگشت #+میدهد، نقطه مقابل قرارداد عادی Bash. echo "Logical Operators" echo x=24 y=25 b=`expr $x = $y` # echo "b = $b" #( $x مساوی $y نیست ) echo a=3 b=`expr $a \> 10` echo 'b=`expr $a \> 10`, therefore...' echo "If a > 10, b = 0 (false)" echo "b = $b" #( 3 بزرگتر از 10 نیست ) echo b=`expr $a \< 10` echo "If a < 10, b = 1 (true)" echo "b = $b" #( 3 کوچکتر از 10 است ) echo #به escape کردن عملگرها توجه کنید. b=`expr $a \<= 3` echo "If a <= 3, b = 1 (true)" echo "b = $b" #( 3 کوچکتر یا مساوی 3 است ) #یک عملگر \>= (بزرگتر یا مساوی) نیز وجود دارد. echo echo # # echo "String Operators" echo a=1234zipper43231 echo "The string being operated upon is \"$a\"." # b=`expr length $a` echo "Length of \"$a\" is $b." # # b=`expr index $a 23` echo "Numerical position of first \"2\" in \"$a\" is \"$b\"." # b=`expr substr $a 2 6` echo "Substring of \"$a\", starting at position 2,\ and 6 chars long is \"$b\"." # #+ # # b=`expr match "$a" '[0-9]*'` # echo Number of digits at the beginning of \"$a\" is $b. b=`expr match "$a" '\([0-9]*\)'` #توجه کنید که پرانتز های escape شده # #+ echo "The digits at the beginning of \"$a\" are \"$b\"." echo exit 0
عملگر : (null) میتواند جایگزین match بشود. به عنوان مثال، b=`expr $a : [0-9]*` معادل دقیقی برای عبارت b=`expr match $a [0-9]*` در مثال فوق است. #!/bin/bash echo echo "String operations using \"expr \$string : \" construct" echo "===================================================" echo a=1234zipper5FLIPPER43231 echo "The string being operated upon is \"`expr "$a" : '\(.*\)'`\"." # # #+ #+ # # #+ echo "Length of \"$a\" is `expr "$a" : '.*'`." # echo "Number of digits at the beginning of \"$a\" is `expr "$a"\ : '[0-9]*'`." # echo echo "The\ digits at the beginning of \"$a\" are `expr "$a" : '\([0-9]*\)'`." # echo "The\ first 7 characters of \"$a\" are `expr "$a" : '\(.......\)'`." # # |
اسکریپت فوق شرح میدهد که expr چگونه پرانتزهای escape شده -- \( ... \) -- عملگر گروهبندی پشت سرهم را با عبارت منظم تحویل شده برای انطباق یک رشته فرعی به کار میبرد. این هم یک مثال دیگر، ایندفعه از «زندگی واقعی».
#زدودن فضای سفید از ابتدا و انتهای قالب %Y-%m-%d تاریخ. LRFDATE=`expr "$LRFDATE" : '[[:space:]]*\([[:digit:]-]*\)[[:space:]]*$'` #از اسکریپت booklistgen.sh نوشته Peter Knowles #+برای تبدیل فایلها به قالب Sony Librie/PRS-50X. #
Perl، sed، و awk دارای وسایل به مراتب عالیتر تجزیه رشته هستند. یک «روتین فرعی» کوتاه sed یا awk درون یک اسکریپت (بخش 36.2 را ببینید) یک جایگزین جالب برای expr است.
برای کاربرد بیشتر expr در عملیات رشتهای بخش 10.1 را مشاهده نمایید.
[1] | و حتی موقعی که xargs مشخصاً لازم نیست، میتواند اجرای فرمانی را که با پردازش دستهای چندین فایل ارتباط دارد، تسریع نماید. |