dirname এবং বেসনাম বনাম প্যারামিটার বিস্তৃতি


20

অন্য রূপের চেয়ে কোনও রূপকে প্রাধান্য দেওয়ার কোনও উদ্দেশ্যমূলক কারণ আছে কি? পারফরম্যান্স, নির্ভরযোগ্যতা, বহনযোগ্যতা?

filename=/some/long/path/to/a_file

parentdir_v1="${filename%/*}"
parentdir_v2="$(dirname "$filename")"

basename_v1="${filename##*/}"
basename_v2="$(basename "$filename")"

echo "$parentdir_v1"
echo "$parentdir_v2"
echo "$basename_v1"
echo "$basename_v2"

উত্পাদন:

/some/long/path/to
/some/long/path/to
a_file
a_file

(ভি 1 শেল প্যারামিটার সম্প্রসারণ ব্যবহার করে, ভি 2 বাহ্যিক বাইনারি ব্যবহার করে))

উত্তর:


21

দুর্ভাগ্যক্রমে দু'জনেরই কৌতুহল আছে।

উভয়ই পসিএক্স দ্বারা প্রয়োজনীয়, সুতরাং তাদের মধ্যে পার্থক্য কোনও বহনযোগ্যতা উদ্বেগ নয় ¹

ইউটিলিটিগুলি ব্যবহার করার সহজ উপায়

base=$(basename -- "$filename")
dir=$(dirname -- "$filename")

পরিবর্তনশীল বিকল্পের পরিবর্তে ডাবল উদ্ধৃতিগুলি সর্বদা হিসাবে নোট করুন এবং --কমান্ডের পরেও, যদি ফাইলের নামটি ড্যাশ দিয়ে শুরু হয় (অন্যথায় কমান্ডগুলি ফাইলের নামটিকে বিকল্প হিসাবে ব্যাখ্যা করবে)। এটি এখনও একটি কিনারা ক্ষেত্রে ব্যর্থ হয়, যা বিরল তবে এটি দূষিত ব্যবহারকারী দ্বারা বাধ্য করা যেতে পারে²: কমান্ড প্রতিস্থাপনটি নতুন লাইনের পিছনে ফেলেছে। সুতরাং যদি কোনও ফাইল নাম কল করা হয় foo/bar␤তবে baseতার barপরিবর্তে সেট করা হবে bar␤। একটি কার্যপ্রণালী হ'ল একটি নন-লাইন অক্ষর যুক্ত করা এবং কমান্ড প্রতিস্থাপনের পরে এটি ছাঁটাই:

base=$(basename -- "$filename"; echo .); base=${base%.}
dir=$(dirname -- "$filename"; echo .); dir=${dir%.}

প্যারামিটার প্রতিস্থাপনের সাথে, আপনি অদ্ভুত অক্ষরগুলির প্রসারণ সম্পর্কিত প্রান্তের কেসগুলিতে দৌড়েন না, তবে স্ল্যাশ চরিত্রটিতে বেশ কয়েকটি সমস্যা রয়েছে। একটি জিনিস যা একেবারে প্রান্তের কেস নয় তা হ'ল ডিরেক্টরি অংশের কম্পিউটিংয়ের ক্ষেত্রে যেখানে নেই সেখানে ক্ষেত্রে বিভিন্ন কোডের প্রয়োজন /

base="${filename##*/}"
case "$filename" in
  */*) dirname="${filename%/*}";;
  *) dirname=".";;
esac

প্রান্তের কেসটি যখন ট্রেলিং স্ল্যাশ হয় (মূল ডিরেক্টরিটি যা সমস্ত স্ল্যাশ সহ অন্তর্ভুক্ত) including basenameএবং dirnameকমান্ড বন্ধ স্ট্রিপ স্ল্যাশ trailing আগে তারা তাদের কাজ। আপনি যদি পসিক্স কনস্ট্রাক্টকে আটকে থাকেন তবে একবারে ট্রিলিং স্ল্যাশগুলি ছিনিয়ে নেওয়ার কোনও উপায় নেই তবে আপনি দুটি পদক্ষেপে এটি করতে পারেন। ইনপুটটিতে স্ল্যাশ ছাড়া কিছুই না থাকলে আপনাকে কেস যত্ন নেওয়া দরকার need

case "$filename" in
  */*[!/]*)
    trail=${filename##*[!/]}; filename=${filename%%"$trail"}
    base=${filename##*/}
    dir=${filename%/*};;
  *[!/]*)
    trail=${filename##*[!/]}
    base=${filename%%"$trail"}
    dir=".";;
  *) base="/"; dir="/";;
esac

যদি আপনি জানতে পারেন যে আপনি কোনও প্রান্তের ক্ষেত্রে নন (উদাহরণস্বরূপ findপ্রারম্ভিক পয়েন্ট ব্যতীত অন্য ফলাফলের মধ্যে সর্বদা একটি ডিরেক্টরি অংশ থাকে এবং এর কোনও অনুপস্থিতি থাকে /) তবে পরামিতি প্রসারিত স্ট্রিং ম্যানিপুলেশনটি সোজা for আপনার যদি সমস্ত প্রান্তের মামলাগুলি মোকাবেলা করার প্রয়োজন হয় তবে ইউটিলিটিগুলি ব্যবহার করা সহজ (তবে ধীর)।

কখনও কখনও, আপনি foo/পছন্দ foo/.না করে পছন্দ করতে চান foo। আপনি যদি কোনও ডিরেক্টরি এন্ট্রিতে অভিনয় করে থাকেন তবে foo/তার সমান হতে হবে foo/., নয় foo; fooএটি কোনও তাত্পর্যপূর্ণ করে যখন কোনও ডিরেক্টরিতে প্রতীকী লিঙ্ক থাকে: fooঅর্থ প্রতীকী লিঙ্ক, foo/মানে লক্ষ্য ডিরেক্টরি। সেক্ষেত্রে পিছনের স্ল্যাশ সহ কোনও .পথের বেসনাম সুবিধাজনকভাবে হয় এবং পথটি তার নিজস্ব নাম হতে পারে।

case "$filename" in
  */) base="."; dir="$filename";;
  */*) base="${filename##*/}"; dir="${filename%"$base"}";;
  *) base="$filename"; dir=".";;
esac

দ্রুত এবং নির্ভরযোগ্য পদ্ধতিটি হিস্ট্রি মডিফায়ারগুলির সাথে zsh ব্যবহার করা (এই প্রথম স্ট্রিপগুলি অনুসরণের স্ল্যাশগুলি যেমন ইউটিলিটিগুলির মতো):

dir=$filename:h base=$filename:t

Less যদি না আপনি সোলারিস 10 এবং তার বেশি বয়সীদের /bin/sh( যেমন এখনও উত্পাদিত মেশিনগুলিতে প্যারামিটার এক্সপেনশন স্ট্রিং ম্যানিপুলেশন বৈশিষ্ট্যগুলির অভাবের মতো প্রাক পসিক্স শেল ব্যবহার করেন না - তবে shইনস্টলেশনটিতে একটি পসিক্স শেল বলা হয়, কেবল এটি /usr/xpg4/bin/shনয় /bin/sh)।
² উদাহরণস্বরূপ: foo␤একটি ফাইল আপলোড পরিষেবাতে ডাকা একটি ফাইল জমা দিন যা এটির বিরুদ্ধে রক্ষা করে না, তারপরে এটি মুছুন এবং fooতার পরিবর্তে মোছার কারণ দিন


কি দারুন. সুতরাং এটির মতো শোনাচ্ছে (কোনও পসিক্স শেলের মধ্যে) সবচেয়ে শক্তিশালী উপায়ে আপনি উল্লেখ করেছেন দ্বিতীয়টি? base=$(basename -- "$filename"; echo .); base=${base%.}; dir=$(dirname -- "$filename"; echo .); dir=${dir%.}? আমি মনোযোগ সহকারে পড়ছিলাম এবং আপনার কোনও অসুবিধাগুলির উল্লেখ আমি লক্ষ্য করি না।
ওয়াইল্ডকার্ড

1
@ উইল্ডকার্ড একটি অপূর্ণতা হ'ল এটি foo/পছন্দ মত fooনয় foo/., যা পসিক্স -অনুবর্তী ইউটিলিটির সাথে সামঞ্জস্যপূর্ণ নয়।
গিলস 'তাই খারাপ হওয়া বন্ধ করুন'

বুঝেছি ধন্যবাদ. আমি মনে করি আমি এখনও সেই পদ্ধতিটিকেই প্রাধান্য দিচ্ছি কারণ আমি জানতে পারি যে আমি ডিরেক্টরিগুলি নিয়ে কাজ করার চেষ্টা করছি কিনা এবং আমি /যদি প্রয়োজন হয় তবে আমি কেবল (বা "ট্যাক্স ব্যাক অন") সামলাতে পারি।
ওয়াইল্ডকার্ড

"উদাহরণস্বরূপ একটি findফলাফল, যা সর্বদা একটি ডিরেক্টরি অংশ ধারণ করে এবং এর কোনও পিছনে নেই /" পুরোপুরি সত্য নয়, প্রথম ফলাফল হিসাবে find ./আউটপুট আসবে ./
তাভিয়ান বার্নস

@ গিলিস নিউলাইন চরিত্রের উদাহরণটি কেবল আমার মনকে উড়িয়ে দিয়েছে। উত্তরের জন্য ধন্যবাদ
স্যাম টমাস

10

উভয়ই পসিক্সে রয়েছে, সুতরাং বহনযোগ্যতা "উচিত" উদ্বেগের বিষয় নয়। শেল বিকল্পগুলি দ্রুত চালানোর জন্য অনুমান করা উচিত।

যাইহোক - এটি পোর্টেবল দ্বারা আপনি কী বোঝাতে চান তার উপর নির্ভর করে। কিছু (অপরিহার্য নয়) পুরানো সিস্টেমগুলি তাদের /bin/sh(সোলারিস 10 এবং তার চেয়ে বেশি পুরোনো মনে রাখে) এই বৈশিষ্ট্যগুলি প্রয়োগ করে না , অন্যদিকে, কিছুক্ষণ আগে, বিকাশকারীদের সতর্ক করা dirnameহয়েছিল যেটি পোর্টেবলের মতো নয় basename

রেফারেন্সের জন্য:

বহনযোগ্যতার কথা বিবেচনা করার ক্ষেত্রে, আমি যে সমস্ত সিস্টেমগুলিতে প্রোগ্রামগুলি বজায় রাখি সেগুলি আমাকে গ্রাহ্য করতে হবে । সবই পসিক্স নয়, তাই ট্রেড অফ রয়েছে। আপনার ট্রেড অফগুলি পৃথক হতে পারে।


7

এছাড়াও আছে:

mkdir '
';    dir=$(basename ./'
');   echo "${#dir}"

0

এর মতো অদ্ভুত স্টাফ ঘটে কারণ প্রচুর ব্যাখ্যা এবং বিশ্লেষণ রয়েছে এবং দুটি প্রক্রিয়া যখন কথা বলে তখন বাকী যা হওয়া দরকার talk কমান্ড বিকল্পগুলি নতুন লাইনগুলি পিছনে ফেলে দেবে। এবং NULs (যদিও এটি এখানে অবশ্যই প্রাসঙ্গিক নয়)basenameএবং dirnameযে কোনও ক্ষেত্রে পিছনে থাকা নিউলাইনগুলিও সরিয়ে ফেলবে কারণ আপনি তাদের সাথে আর কীভাবে কথা বলবেন? আমি জানি, একটি ফাইলনামে নতুন লাইনগুলি হ'ল যাইহোক এক রকম অ্যানেথেমার, তবে আপনি কখনই জানেন না। আপনি যখন অন্যথায় করতে পারতেন তখন সম্ভবত ত্রুটিযুক্ত পথে যাওয়ার অর্থ হবে না।

এখনও ... ${pathname##*/} != basenameএবং তেমনি ${pathname%/*} != dirname। এই আদেশগুলি তাদের নির্দিষ্ট ফলাফলগুলিতে পৌঁছানোর জন্য বেশিরভাগ সু-সংজ্ঞায়িত ক্রমগুলি সম্পাদন করতে নির্দিষ্ট করা হয়েছে ।

অনুমানটি নীচে রয়েছে, তবে প্রথমে এখানে টিডার সংস্করণ রয়েছে:

basename()
    case   $1   in
    (*[!/]*/)     basename         "${1%"${1##*[!/]}"}"   ${2+"$2"}  ;;
    (*/[!/]*)     basename         "${1##*/}"             ${2+"$2"}  ;;
  (${2:+?*}"$2")  printf  %s%b\\n  "${1%"$2"}"       "${1:+\n\c}."   ;;
    (*)           printf  %s%c\\n  "${1##///*}"      "${1#${1#///}}" ;;
    esac

এটা একটা সম্পূর্ণরূপে POSIX অনুবর্তী এর basenameসহজ মধ্যে sh। এটি করা কঠিন নয়। আমি নীচে আমি ব্যবহার করি এমন কয়েকটি দম্পতি একত্রিত করেছি কারণ ফলাফলের উপর প্রভাব ফেলতে পারছিলাম না।

এখানে অনুমান:

basename()
    case   $1 in
    ("")            #  1. If  string  is  a null string, it is 
                    #     unspecified whether the resulting string
                    #     is '.' or a null string. In either case,
                    #     skip steps 2 through 6.
                  echo .
     ;;             #     I feel like I should flip a coin or something.
    (//)            #  2. If string is "//", it is implementation-
                    #     defined whether steps 3 to 6 are skipped or
                    #     or processed.
                    #     Great. What should I do then?
                  echo //
     ;;             #     I guess it's *my* implementation after all.
    (*[!/]*/)       #  3. If string consists entirely of <slash> 
                    #     characters, string shall be set to a sin‐
                    #     gle <slash> character. In this case, skip
                    #     steps 4 to 6.
                    #  4. If there are any trailing <slash> characters
                    #     in string, they shall be removed.
                  basename "${1%"${1##*[!/]}"}" ${2+"$2"}  
      ;;            #     Fair enough, I guess.
     (*/)         echo /
      ;;            #     For step three.
     (*/*)          #  5. If there are any <slash> characters remaining
                    #     in string, the prefix of string up to and 
                    #     including the last <slash> character in
                    #     string shall be removed.
                  basename "${1##*/}" ${2+"$2"}
      ;;            #      == ${pathname##*/}
     ("$2"|\
      "${1%"$2"}")  #  6. If  the  suffix operand is present, is not
                    #     identical to the characters remaining
                    #     in string, and is identical to a suffix of
                    #     the characters remaining  in  string, the
                    #     the  suffix suffix shall be removed from
                    #     string.  Otherwise, string is not modi‐
                    #     fied by this step. It shall not be
                    #     considered an error if suffix is not 
                    #     found in string.
                  printf  %s\\n "$1"
     ;;             #     So far so good for parameter substitution.
     (*)          printf  %s\\n "${1%"$2"}"
     esac           #     I probably won't do dirname.

... সম্ভবত মন্তব্যগুলি বিভ্রান্ত করছে ....


1
বাহ, ফাইলনামগুলিতে নতুন লাইনের পিছনে থাকা ভাল বিষয়। কী কী কৃমি। যদিও আমি মনে করি না আমি সত্যিই আপনার স্ক্রিপ্টটি বুঝতে পেরেছি। আমি এর [!/]আগে কখনও দেখিনি , এমন [^/]কি? তবে এর সাথে আপনার মন্তব্যটির সাথে এটি মিলছে বলে মনে হচ্ছে না ....
ওয়াইল্ডকার্ড

1
@ উইল্ডকার্ড - ভাল .. এটি আমার মন্তব্য নয়। এটা মান । এর জন্য পসিক্স স্পেক basenameহ'ল এটি আপনার শেল দিয়ে কীভাবে করা যায় সে সম্পর্কে নির্দেশাবলীর একটি সেট। তবে গ্লোবগুলির [!charclass]সাথে এটি করার পোর্টেবল উপায় [^class]হ'ল রেইগেক্সের জন্য - এবং শেলগুলি রেইগেক্সের জন্য নির্দিষ্ট করা হয় না। আমাদের সম্পর্কে মন্তব্য মিলে ... caseফিল্টার, তাই যদি আমি একটি স্ট্রিং যা একটি trailing স্ল্যাশ রয়েছে মেলে / এবং একটি !/তারপর যদি পরবর্তী ক্ষেত্রে প্যাটার্ন নিচের সাথে মেলে এমন যেকোনো চিহ্ন /স্ল্যাশ এ সব তারা শুধুমাত্র হতে পারে সব স্ল্যাশ। এবং এর নীচের একটিতে
মাইক্রোসার্ভ

2

প্রক্রিয়া থেকে আপনি একটি উত্সাহ পেতে পারেন basenameএবং dirname(কেন এই বিল্টিনগুলি হয় না তা আমি বুঝতে পারি না - যদি এগুলি প্রার্থী না হয় তবে আমি কী তা জানি না) তবে বাস্তবায়নের জন্য এই জাতীয় জিনিসগুলি পরিচালনা করা দরকার:

path         dirname    basename
"/usr/lib"    "/usr"    "lib"
"/usr/"       "/"       "usr"
"usr"         "."       "usr"
"/"           "/"       "/"
"."           "."       "."
".."          "."       ".."

Base বেসনাম থেকে (3)

এবং অন্যান্য প্রান্তের কেস

আমি ব্যবহার করছি:

basename(){ 
  test -n "$1" || return 0
  local x="$1"; while :; do case "$x" in */) x="${x%?}";; *) break;; esac; done
  [ -n "$x" ] || { echo /; return; }
  printf '%s\n' "${x##*/}"; 
}

dirname(){ 
  test -n "$1" || return 0
  local x="$1"; while :; do case "$x" in */) x="${x%?}";; *) break;; esac; done
  [ -n "$x" ] || { echo /; return; }
  set -- "$x"; x="${1%/*}"
  case "$x" in "$1") x=.;; "") x=/;; esac
  printf '%s\n' "$x"
}

(জিএনইউতে আমার সর্বশেষ বাস্তবায়ন basenameএবং dirnameএকাধিক যুক্তি বা প্রত্যয় স্ট্রাইপিংয়ের মতো জিনিসগুলির জন্য কিছু বিশেষ অভিনব কমান্ড লাইন স্যুইচগুলি যুক্ত করে তবে শেলের মধ্যে এটি যোগ করা খুব সহজ))

এগুলি bashবিল্টিনগুলিতে তৈরি করা এতটা কঠিন নয় (অন্তর্নিহিত সিস্টেম প্রয়োগের মাধ্যমে) তবে উপরের ফাংশনটি সংকলন করা দরকার নয় এবং তারা কিছুটা উত্সাহও সরবরাহ করে।


প্রান্তের মামলার তালিকাটি আসলে খুব সহায়ক। এগুলি সব খুব ভাল পয়েন্ট। তালিকাটি মোটামুটি সম্পূর্ণ বলে মনে হচ্ছে; সত্যিই কি অন্য কোন প্রান্তের মামলা আছে?
ওয়াইল্ডকার্ড

আমার আগের প্রয়োগটি x//ঠিক মতো জিনিসগুলি পরিচালনা করে না , তবে উত্তর দেওয়ার আগে আমি আপনার জন্য স্থির করেছি। আমি আশা করি এটিই
PSkocik

ফাংশন এবং এক্সিকিউটেবলগুলি এই উদাহরণগুলিতে কী করে তা তুলনা করতে আপনি একটি স্ক্রিপ্ট চালাতে পারেন। আমি একটি 100% ম্যাচ পাচ্ছি।
PSkocik

1
আপনার ডায়ারাম ফাংশনটি বার বার স্ল্যাশের ঘটনাগুলি কেড়ে নেবে বলে মনে হচ্ছে না। যেমন: dirname a///b//c//d////eফলন a///b//c//d///
কোডফোরস্টার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.