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

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

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

#                               نمایش ‎$@‎ ‏(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‎ با مهربانی به من اجازه داد از اسکریپت‌های آموزنده‌اش استفاده کنم.

انجام آن به طور صحیح: یک خلاصه کوتاه توسط ‎David Wheeler‎ ‎http://www.dwheeler.com/essays/filenames-in-shell.html‎ بنابراین، چگونه می‌توانید در پوسته نام فایل‌ها را به طور صحیح پردازش کنید؟ خلاصه‌ای در باره چگونگی انجام صحیح آن، برای ناشکیبایی که الساعه جواب می‌خواهد. به طور مختصر: نقل‌قول دوگانه و به کار بردن ‎"$variable"‎ به جای ‎$variable‎، تنظیم ‎IFS‎ فقط به سطر جدید و ‎tab‎، پیشوند کردن تمام ‎globها-نام فایل‌هابه طوری که نتوانند موقعی که بسط یافتند با«-» شروع بشوند، و استفاده از الگوهایی که به درستی کار می‌کنند. این هم چند نمونه از الگوهایی که به طور صحیح کار می‌کنند:

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