تجزیه و مدیریت نام مسیرها

پیوست ‎D‎- تجزیه و مدیریت نام مسیرها

‎Emmanual Rouat‎ مثال پایین را در مورد تجزیه و تبدیل نام فایل‌ها و بویژه، نام مسیرها، اهدا نموده است. این مثال به استفاده زیاد از توانایی‌های sed نزدیک می‌شود.

#!/usr/bin/env bash
#--------------------------------------------------------------
#            مدیریت متغیرهای ‎PATH‎،‏ ‎LD_LIBRARY_PATH‎،‏ ‎MANPATH‎ ...
#                  نوشته شده به وسیله ‎Emmanuel Rouat‎ ‏‎<no-email>‎
#        (ملهم از مستندات «pathfuncs» در Bash و از این گفتار‌ها:
# 
# 
#                   آخرین ویرایش: ‎Sat Sep 22 12:01:55 CEST 2012‎
#
#              توابع پایین فاصله‌ها را به طور صحیح اداره می‌کنند.
# گمان می‌کنم جای این توابع در عوض ‎.bashrc‎ در ‎.bash_profile‎ است.
#
#جنبه پیمانه‌ای بودن این توابع گسترش آنها را به مدیریت جایگزینی 
#                        مسیر به جای پاک کردن مسیر، آسان می‌سازد
#
# جهت توضیح پاک کردن «اقلام دوتایی»، مدخل ‎43‎ در اینجا را ببینید:
#
#                                            (ترفند جالبی است!)
#--------------------------------------------------------------

#                               نمایش ‎$@‎ ‏(PATH معمولی) در لیست.
function p_show() { local p="$@" && for p; do [[ ${!p} ]] &&
echo -e ${!p//:/\\n}; done }

#    فیلتر کردن سطرهای خالی، slashهای متعدد ، و مدخل‌های تکراری.
function p_filter()
{ awk '/^[ \t]*$/ {next} {sub(/\/+$/, "");gsub(/\/+/, "/")}!x[$0]++' ;}

#  ساختن دوباره لیستی از مدخل‌ها با جداکننده «:» (شبیه به PATH).
function p_build() { paste -sd: ;}

#                     پاک کردن ‎$1‎ ‏(PATH نوعی)‏ و دوباره ساختن آن
function p_clean()
{ local p=${1} && eval ${p}='$(p_show ${p} | p_filter | p_build)' ;}

#      حذف ‎$1‎ از ‎$2‎ (مبتنی بر ‎stackoverflow‎، همراه با تغییرات).
function p_rm()
{ local d=$(echo $1 | p_filter) p=${2} &&
  eval ${p}='$(p_show ${p} | p_filter | grep -xv "${d}" | p_build)' ;}

# مانند مورد قبل، اما فیلتر بر روی یک الگو (خطرناک، از bin یا /
#+                               به عنوان الگو استفاده نکنید!).
function p_rmpat()
{ local d=$(echo $1 | p_filter) p=${2} && eval ${p}='$(p_show ${p} |
  p_filter | grep -v "${d}" | p_build)' ;}
#                  حذف ‎$1‎ از ‎$2‎ و الحاق نمودن آن به طور پاکیزه.
function p_append()
{ local d=$(echo $1 | p_filter) p=${2} && p_rm "${d}" ${p} &&
  eval ${p}='$(p_show ${p} d | p_build)' ;}

#           حذف ‎$1‎ از ‎$2‎ و پیوست آن به طور پاکیزه به ابتدای ‎$2‎.
function p_prepend()
{ local d=$(echo $1 | p_filter) p=${2} && p_rm "${d}" ${p} &&
  eval ${p}='$(p_show d ${p} | p_build)' ;}

#                    چند آزمایش:
echo
MYPATH="/bin:/usr/bin/:/bin://bin/"
p_append "/project//my project/bin" MYPATH
echo "Append '/project//my project/bin' to '/bin:/usr/bin/:/bin://bin/'"
echo "(result should be: /bin:/usr/bin:/project/my project/bin)"
echo $MYPATH

echo
MYOTHERPATH="/bin:/usr/bin/:/bin:/project//my project/bin"
p_prepend "/project//my project/bin" MYOTHERPATH
echo "Prepend '/project//my project/bin' \
to '/bin:/usr/bin/:/bin:/project//my project/bin/'"
echo "(result should be: /project/my project/bin:/bin:/usr/bin)"
echo $MYOTHERPATH

echo
p_prepend "/project//my project/bin" FOOPATH  # ‎FOOPATH‎ وجود ندارد.
echo "Prepend '/project//my project/bin' to an unset variable"
echo "(result should be: /project/my project/bin)"
echo $FOOPATH

echo
BARPATH="/a:/b/://b c://a:/my local pub"
p_clean BARPATH
echo "Clean BARPATH='/a:/b/://b c://a:/my local pub'"
echo "(result should be: /a:/b:/b c:/my local pub)"
echo $BARPATH

***

‎David Wheeler‎ با مهربانی به من اجازه داد از اسکریپت‌های آموزنده‌اش استفاده کنم.


 IFS="$(printf '\n\t')"
 #  پاک کردن ‎SPACE‎، بنابراین با نام فایل‌های دارای فاصله خوب کار می‌کند.

 #                                               استفاده صحیح از ‎glob‎:
 #+ همیشه حلقه ‎for، پیشوند برای ‎glob‎، و کنترل موجودیت را به کار ببرید:
 for file in ./* ; do          # از «‎./*‎» استفاده کنید، از * خالی هرگز
   if [ -e "$file" ] ; then    #  مطمئن شوید حداقل یک تطابق موجود است.
     COMMAND ... "$file" ...
   fi
 done

 # اما استفاده صحیح از glob نیازمند الحاقیه‌های غیر استاندارد ‎bash‎ است.
 shopt -s nullglob  #    الحاقیه Bash
                    #+   به طوری که globهای بدون تطابق خوب کار می‌کنند.
 for file in ./* ; do        # از «‎./*‎» استفاده کنید، از «*» خالی هرگز
   COMMAND ... "$file" ...
 done

 #  این فرمانها، تمام نام فایل‌ها را به درستی اداره می‌کنند، اگر COMMAND
 #+                         بزرگ باشد می‌توانند سنگین و بد قواره بشوند:
 find ... -exec COMMAND... {} \;
 find ... -exec COMMAND... {} \+  #اگر COMMAND چند فایل را قبول می‌کند.

 # این کد نام فایل‌های دارای کاراکترهای کنترلی (از جمله سطر جدید و tab)
 #+                                                را از قلم می‌اندازد.
 IFS="$(printf '\n\t')"
 controlchars="$(printf '*[\001-\037\177]*')"
 for file in $(find . ! -name "$controlchars"') ; do
   COMMAND "$file" ...
 done

 #      اگر نام فایل‌ها نتوانند شامل tabها و سطر جدید باشند قبول است --
 #+                                                   متوجه فرض باشید.
 IFS="$(printf '\n\t')"
 for file in $(find .) ; do
   COMMAND "$file" ...
 done

 #     به الحاقیه‌های غیر استاندارد اما رایج در find و xargs نیاز دارد:
 find . -print0 | xargs -0 COMMAND

 # الحاقیه‌های غیر استاندارد find و shell را لازم دارد (bash کار می‌کند).
 #           وقتی حلقه به پایان می‌رسد شاید متغیرها برقرار باقی نمانند:
 find . -print0 | while IFS="" read -r -d "" file ; do ...
   COMMAND "$file" #همه جا از ‎"$file"‎ نقل‌قولی استفاده کنید نه از ‎$file
 done

 #الحاقیه‌های غیر استاندارد find و shell را نیاز دارد (bash کار می‌کند).
 #  سیستم مورد استفاده باید دارای لوله های با نام (FIFOها)، یا مکانیسم
 #+                                                      ‎/dev/fd‎ باشد.
 # در این نگارش، متغیرها پس از خاتمه یافتن حلقه بر قرار می‌مانند، و شما
 #                                          می‌توانید از stdin بخوانید.
 #+   (در صورتیکه ‎fd‎ شماره ‎4‎ لازم می‌شود، ‎4‎ را به عدد دیگری تغییر دهید.)

 while IFS="" read -r -d "" file <&4 ; do
   COMMAND "$file"   # همه جا ‎"$file"‎ نقل‌قولی به کار ببرید -- نه ‎$file‎
 done 4< <(find . -print0)

 #                                                  نگارش لوله با نام.
 #  مستلزم الحاقیه‌های غیراستاندارد find و read پوسته (bash کار می‌کند).
 #   سیستم مورد استفاده باید در بر دارنده لوله‌های با نام (FIFOها)‏باشد.
 # یکبار دیگر، در این نگارش، متغیرها پس از پایان حلقه برقرار می‌مانند و
 #                                      شما می‌توانید از stdin بخوانید.
 #      (اگر ‎fd‎ شماره ‎4‎ مورد نیاز است، ‎4‎ را به عدد دیگری تغییر بدهید).

 mkfifo mypipe

 find . -print0 > mypipe &
 while IFS="" read -r -d "" file <&4 ; do
   COMMAND "$file"   # همه جا ‎"$file"‎ نقل‌قولی به کار ببرید -- نه ‎$file‎
 done 4< mypipe