বেসের সর্বশেষ 3 টি অক্ষর (বিয়োগ প্রত্যয়) ফাইলেরাম উত্তোলনের সবচেয়ে ছোট উপায়


12

আমি (বেস নাম আমি পথ ছাড়া অর্থ দ্বারা একটি ফাইলের বেস নামের শেষ 3 অক্ষরের একটি SH লিপিতে একটি পরিবর্তনশীল সেট করতে চেষ্টা করছি এবং প্রত্যয় ছাড়াই)। আমি এটি করতে সফল হয়েছি তবে, কৌতূহলের বাইরে, আমি ভাবছি যে আমি ব্যবহার করতে পারি এমন একটি সংক্ষিপ্ত, একক কমান্ড আছে কিনা। মূলত আমার সাথে ওয়ান-লাইনার ছিল awk, তবে এটি দীর্ঘ ছিল। বর্তমানে আমার কাছে এই দ্বি-লাইনের স্ক্রিপ্ট রয়েছে (ধরে নিলাম একটি সম্পূর্ণ ফাইলের নাম রয়েছে $1):

filebase=`basename "$1"`
lastpart=`echo -n ${filebase%.*} | tail -c3`

সুতরাং উদাহরণস্বরূপ, "/path/to/somefile.txt" "আইলে" এর সাথে শেষ হয় $lastpart

আমি কী একরকম একত্রিত করতে পারি basenameএবং প্রত্যয়টিকে একটি একক কমান্ডের মধ্যে ফালাটি বিট করতে পারি এবং tailপাইপ ব্যবহার না করে এটিকে (বা আমি ব্যবহার করতে পারি এমন কিছু) পাঠানোর কোনও উপায় আছে কি? প্রত্যয়টি অজানা তাই আমি এটির পরামিতি হিসাবে বেস করতে পারি না basename

মূল লক্ষ্যটি যতটা সম্ভব সংক্ষিপ্ত হওয়া যতটা সম্ভব তত সংক্ষিপ্ত হওয়া সম্ভব নয়। এই সমস্তের আসল প্রসঙ্গটি সুপারউসার সম্পর্কে এই প্রশ্ন , যেখানে আমি যুক্তিসঙ্গত সহজ উত্তরটি নিয়ে আসার চেষ্টা করছি।


2
আপনি যেমন একটি ফাইল সঙ্গে ডিল করতে হবে file.one.two.three? আপনি চাইবেন ileনাকি two?
টেরডন

@terdon twoকাজ করবে; আমার বোধ হয় তার উপর বাড়ানো হবে .three
জেসন সি

উত্তর:


6

এটি একটি সাধারণ কাজ expr:

$ file=/path/to/abcdef.txt
$ expr "/$file" : '.*\([^/.]\{3\}\)\.[^/.]*$'
def

যদি আপনি জানেন যে আপনার ফাইলের নামগুলির প্রত্যাশিত বিন্যাস রয়েছে (এতে একটি এবং একটি মাত্র বিন্দু এবং বিন্দুর আগে কমপক্ষে 3 টি অক্ষর থাকে) তবে এটিকে সহজ করা যায়:

expr "/$file" : '.*\(.\{3\}\)\.'

নোট করুন যে কোনও প্রকার ম্যাচ না থাকলে প্রস্থান স্থিতি শূন্য হবে না, তবে ম্যাচ করা অংশটি এমন একটি সংখ্যাও রয়েছে যা 0 এ সমাধান হয়েছে (যেমন a000.txtবা এর জন্য a-00.txt)

সহ zsh:

file=/path/to/abcdef.txt
lastpart=${${file:t:r}[-3,-1]}

( লেজের:t জন্য (বেসনাম), বিশ্রামের জন্য (এক্সটেনশন সরানো সহ)):r


2
খুশী হলাম। exprআমার সাথে পরিচিত হওয়া দরকার অন্যটি। আমি সত্যিই মত zshসাধারণভাবে সমাধান (আমি শুধু একটি বামদিকে নেস্টেড বদল তার সমর্থনের বিষয়ে পড়া ছিল ${}খুব গতকাল এবং তবেই shএকই ছিল), এটা শুধু একটা অকর্মা এটি সবসময় ডিফল্টরূপে উপস্থিত নেই না।
জেসন সি

2
@ জেসনসি - তথ্যটি সবচেয়ে গুরুত্বপূর্ণ। এটির যথাসাধ্য যতটা সম্ভব অ্যাক্সেসযোগ্য করুন - যাইহোক সিস্টেমের পুরো পয়েন্টটি চালিয়ে যায়। যদি রেপ খাবার কিনে থাকে তবে আমি মন খারাপ করতে পারি তবে প্রায়শই (কখনও নয়) তথ্যটি বেকন এনে দেয়
মাইকজার্ভ

1
@ মিমকিজার "অনুরোধ: বেকন জন্য এক্সচেঞ্জ প্রতিনিধি"; এখানে এসে মেটা দেখুন।
জেসন সি

1
@ মিকার্সেরভে, আপনার পসিএক্স, কেবল বিল্টইন ব্যবহার করে এবং কোনও প্রক্রিয়া কাঁটাচামচ করে না। কমান্ড সাবস্টিটিউশন ব্যবহার না করা এর অর্থ আপনি পিছনে থাকা নতুন লাইনের সমস্যাগুলি এড়াতে পারেন, সুতরাং এটিও একটি ভাল উত্তর।
স্টাফেন চেজেলাস

1
@ মাইকজার্ভ, আমি বোঝাতে চাইছি না পসিক্স exprছিল না । এটি নিশ্চিতভাবে. যদিও এটি অন্তর্নির্মিত খুব কমই।
স্টাফেন চেজেলাস

13
var=123456
echo "${var#"${var%???}"}"

###OUTPUT###

456

এটি প্রথমে শেষ তিনটি অক্ষর $varসরিয়ে দেয় তারপরে $varসেই অপসারণের ফলাফলগুলি থেকে সরিয়ে দেয় - যা শেষ তিনটি অক্ষরকে প্রদান করে $var। আপনি কীভাবে এমন কাজ করতে পারেন তা প্রদর্শনের উদ্দেশ্যে এখানে আরও কয়েকটি উদাহরণ দেওয়া হয়েছে:

touch file.txt
path=${PWD}/file.txt
echo "$path"

/tmp/file.txt

base=${path##*/}
exten=${base#"${base%???}"}
base=${base%."$exten"}
{ 
    echo "$base" 
    echo "$exten" 
    echo "${base}.${exten}" 
    echo "$path"
}

file
txt
file.txt
/tmp/file.txt

আপনাকে এতগুলি কমান্ডের মাধ্যমে এগুলি ছড়িয়ে দিতে হবে না। আপনি এটি কম্প্যাক্ট করতে পারেন:

{
    base=${path##*/} exten= 
    printf %s\\n "${base%.*}" "${exten:=${base#"${base%???}"}}" "$base" "$path"
    echo "$exten"
}

file 
txt 
file.txt 
/tmp/file.txt
txt

টিং শেল প্যারামিটারগুলির $IFSসাথে সংমিশ্রণ setশেল ভেরিয়েবলগুলির মাধ্যমে পার্সিং এবং তুরপুনের একটি খুব কার্যকর উপায় হতে পারে:

(IFS=. ; set -f; set -- ${path##*/}; printf %s "${1#"${1%???}"}")

আপনি শুধুমাত্র তিনটি অক্ষর অবিলম্বে গত পর প্রথম সময়ের পূর্ববর্তী পাবেন /মধ্যে $path। আপনি অবিলম্বে গত পূর্ববর্তী শুধুমাত্র প্রথম তিনটি অক্ষর উদ্ধার করতে চান তাহলে .$path (উদাহরণস্বরূপ, যদি একাধিক একটি সম্ভাবনা আছে .ফাইলের নাম নেই) :

(IFS=.; set -f; set -- ${path##*/}; ${3+shift $(($#-2))}; printf %s "${1#"${1%???}"}")

উভয় ক্ষেত্রে আপনি করতে পারেন:

newvar=$(IFS...)

এবং...

(IFS...;printf %s "$2")

... যা অনুসরণ করে তা মুদ্রণ করবে .

আপনার যদি কোনও বাহ্যিক প্রোগ্রাম ব্যবহার করতে আপত্তি না থাকে তবে আপনি তা করতে পারেন:

printf %s "${path##*/}" | sed 's/.*\(...\)\..*/\1/'

যদি \nফাইলনেমে কোনও ইলাইন চরিত্রের সম্ভাবনা থাকে (দেশীয় শেল সমাধানের জন্য প্রযোজ্য নয় - তারা সকলেই যেভাবে হ্যান্ডেল করে) :

printf %s "${path##*/}" | sed 'H;$!d;g;s/.*\(...\)\..*/\1/'

1
ধন্যবাদ, ধন্যবাদ আমি ডকুমেন্টেশনও পেয়েছি । তবে $baseসেখান থেকে সর্বশেষ 3 টি চরিত্র পেতে , আমি তিনটি লাইনটি সেরা করতে পারি name=${var##*/} ; base=${name%%.*} ; lastpart=${base#${base%???}}। প্লাস দিকে এটি খাঁটি বাশ, তবে এটি এখনও 3 লাইন। (আপনার "/tmp/file.txt" উদাহরণে "ফাইল" না দিয়ে আমার "আইলে" দরকার ছিল)) পরামিতি প্রতিস্থাপন সম্পর্কে আমি কেবল অনেক কিছু শিখেছি; আমি বুঝতে পারি না যে এটি করতে পারে ... বেশ সহজ। আমি এটিকে ব্যক্তিগতভাবে ব্যক্তিগতভাবে খুব পঠনযোগ্য বলে মনে করি।
জেসন সি

1
@ জেসনসি - এটি সম্পূর্ণরূপে বহনযোগ্য আচরণ - এটি বাশ নির্দিষ্ট নয়। আমি পড়া সুপারিশ এই
মাইকজার্ভ

1
ঠিক আছে, আমার ধারণা, প্রত্যয়টি সরিয়ে ফেলার %পরিবর্তে আমি ব্যবহার করতে %%পারি এবং আমার আসলে পথটি সরানোর দরকার নেই, তাই আমি আরও ভাল, দুটি লাইন পেতে পারি noextn=${var%.*} ; lastpart=${noextn#${noextn%???}}
জেসন সি

1
@ জেসনসি - হ্যাঁ, দেখে মনে হচ্ছে এটি কার্যকর হবে। যদি সেখানে থাকে এবং আপনি প্রসারণটি উদ্ধৃতি না $IFSকরেন তবে এটি ভেঙে যাবে ${noextn}। সুতরাং, এটি নিরাপদ:lastpart=${noextn#"${noextn%???}"}
মাইক্রোজার

1
@JasonC - শেষ, যদি আপনি সহায়ক উপরে পাওয়া যায় নি, আপনি তাকান করতে চাইবেন এই । এটি প্যারামিটার সম্প্রসারণের অন্যান্য ফর্মগুলির সাথে সম্পর্কিত এবং সেই প্রশ্নের অন্যান্য উত্তরগুলিও খুব ভাল। এবং একই বিষয়ের মধ্যে অন্য দুটি উত্তরের লিঙ্ক রয়েছে। তুমি যদি চাও.
মাইক্রজারভ

4

আপনি যদি ব্যবহার করতে পারেন perl:

lastpart=$(
    perl -e 'print substr((split(/\.[^.]*$/,shift))[0], -3, 3)
            ' -- "$(basename -- "$1")"
)

এটা অসাধারণ. ভোট পেয়েছি
মাইক্রজারভ

একটি বিট আরো সংক্ষিপ্ত: perl -e 'shift =~ /(.{3})\.[^.]*$/ && print $1' $filenamebasenameফাইলের নামের কোনও প্রত্যয় না থাকলেও পথের কিছু ডিরেক্টরি থাকতে পারে তবে অতিরিক্ত প্রয়োজন হবে।
দুবু

@ ডাবু: ফাইলের নামটির প্রত্যয় না থাকলে আপনার সমাধান সর্বদা ব্যর্থ হয়।
কিউংলম

1
@ জ্ঞাক এটি উদ্দেশ্য দ্বারা ছিল। তবে আপনি ঠিক বলেছেন, উদ্দেশ্য অনুসারে এটি ভুল হতে পারে। বিকল্প:perl -e 'shift =~ m#(.{3})(?:\.[^./]*)?$# && print $1' $filename
দুবু

2

sed এটির জন্য কাজ করে:

[user@host ~]$ echo one.two.txt | sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|'
two

অথবা

[user@host ~]$ sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|' <<<one.two.txt
two

আপনার যদি sedসমর্থন করে না -r, শুধু দৃষ্টান্ত প্রতিস্থাপন ()সঙ্গে \(এবং \), এবং তারপর -rপ্রয়োজন হয় না।


1

যদি পার্ল উপলভ্য থাকে তবে আমি দেখতে পাচ্ছি যে এটি অন্যান্য সমাধানের চেয়ে বেশি পঠনযোগ্য হতে পারে, বিশেষত কারণ এর রেজেক্স ভাষাটি আরও /xভাববাদী এবং এতে সংশোধক রয়েছে, যা পরিষ্কার রেজেেক্সগুলি লেখার অনুমতি দেয়:

perl -e 'print $1 if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"

এ জাতীয় কোনও মিল না থাকলে এটি কিছুই মুদ্রণ করে না (যদি বেসনামটির কোনও এক্সটেনশন না থাকে বা এক্সটেনশনের আগে রুটটি খুব ছোট থাকে) is আপনার প্রয়োজনীয়তার উপর নির্ভর করে আপনি রেজেক্স সামঞ্জস্য করতে পারেন। এই রেজেক্স সীমাবদ্ধতাগুলি প্রয়োগ করে:

  1. এটি চূড়ান্ত সম্প্রসারণের আগে 3 টি অক্ষরের সাথে মেলে (শেষ বিন্দুটির পরে এবং অংশ)। এই 3 টি অক্ষরে একটি বিন্দু থাকতে পারে।
  2. এক্সটেনশনটি ফাঁকা থাকতে পারে (বিন্দু বাদে)।
  3. মিলে যাওয়া অংশ এবং এক্সটেনশনটি অবশ্যই বেসনামের অংশ (শেষ স্ল্যাশের পরে অংশ) হওয়া উচিত।

কমান্ড প্রতিস্থাপনে এটি ব্যবহার করার ফলে অনেকগুলি অনুধাবন করা নিউলাইনগুলি সরিয়ে ফেলার স্বাভাবিক সমস্যা রয়েছে, এটি স্টাফেনের উত্তরকেও প্রভাবিত করে। উভয় ক্ষেত্রেই এটি মোকাবেলা করা যেতে পারে, তবে এখানে একটু সহজ:

lastpart=$(
  perl -e 'print "$1x" if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
)
lastpart=${lastpart%x}  # allow for possible trailing newline

0

Python2.7

$ echo /path/to/somefile.txt | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
ile

$ echo file.one.two.three | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
two

0

আমি মনে করি এই ব্যাশ ফাংশন, প্যাথএসআরটি (), আপনি যা খুঁজছেন তা করবে।

এর জন্য বিশ্রী, সেড, গ্রেপ, পারল বা এক্সপ্রেসের প্রয়োজন নেই। এটি কেবল ব্যাশ বিল্টিন ব্যবহার করে তাই এটি বেশ দ্রুত।

আমি নির্ভরশীল আরগস নাম্বার এবং আইসঅফশন ফাংশনগুলিও অন্তর্ভুক্ত করেছি তবে তাদের কার্যকারিতা সহজেই প্যাথস্ট্রিটিতে সংহত করা যেতে পারে।

টার্মিনাল কমান্ডলাইনে বা YAD এর মাধ্যমে জিইউআই ডায়ালগ বাক্সে সহায়তা পাঠ্য আউটপুট দেওয়ার জন্য অনেকগুলি নির্ভরশীলতা হেল্পশোটি অন্তর্ভুক্ত না থাকলে নির্ভরশীল ফাংশনটি অন্তর্ভুক্ত নয় । এতে পাঠানো সহায়তা পাঠ্যটি ডকুমেন্টেশনের জন্য অন্তর্ভুক্ত রয়েছে। আপনি যদি হেল্পশো এবং এর নির্ভরশীলদের চান তবে পরামর্শ দিন।

function  pathStr () {
  ifHelpShow "$1" 'pathStr --OPTION FILENAME
    Given FILENAME, pathStr echos the segment chosen by --OPTION of the
    "absolute-logical" pathname. Only one segment can be retrieved at a time and
    only the FILENAME string is parsed. The filesystem is never accessed, except
    to get the current directory in order to build an absolute path from a relative
    path. Thus, this function may be used on a FILENAME that does not yet exist.
    Path characteristics:
        File paths are "absolute" or "relative", and "logical" or "physical".
        If current directory is "/root", then for "bashtool" in the "sbin" subdirectory ...
            Absolute path:  /root/sbin/bashtool
            Relative path:  sbin/bashtool
        If "/root/sbin" is a symlink to "/initrd/mnt/dev_save/share/sbin", then ...
            Logical  path:  /root/sbin/bashtool
            Physical path:  /initrd/mnt/dev_save/share/sbin/bashtool
                (aka: the "canonical" path)
    Options:
        --path  Absolute-logical path including filename with extension(s)
                  ~/sbin/file.name.ext:     /root/sbin/file.name.ext
        --dir   Absolute-logical path of directory containing FILENAME (which can be a directory).
                  ~/sbin/file.name.ext:     /root/sbin
        --file  Filename only, including extension(s).
                  ~/sbin/file.name.ext:     file.name.ext
        --base  Filename only, up to last dot(.).
                  ~/sbin/file.name.ext:     file.name
        --ext   Filename after last dot(.).
                  ~/sbin/file.name.ext:     ext
    Todo:
        Optimize by using a regex to match --options so getting argument only done once.
    Revised:
        20131231  docsalvage'  && return
  #
  local _option="$1"
  local _optarg="$2"
  local _cwd="$(pwd)"
  local _fullpath=
  local _tmp1=
  local _tmp2=
  #
  # validate there are 2 args and first is an --option
  [[ $(argsNumber "$@") != 2 ]]                        && return 1
  ! isOption "$@"                                      && return 1
  #
  # determine full path of _optarg given
  if [[ ${_optarg:0:1} == "/" ]]
  then
    _fullpath="$_optarg"
  else
    _fullpath="$_cwd/$_optarg"
  fi
  #
  case "$_option" in
   --path)  echo "$_fullpath"                            ; return 0;;
    --dir)  echo "${_fullpath%/*}"                       ; return 0;;
   --file)  echo "${_fullpath##*/}"                      ; return 0;;
   --base)  _tmp1="${_fullpath##*/}"; echo "${_tmp1%.*}" ; return 0;;
    --ext)  _tmp1="${_fullpath##*/}";
            _tmp2="${_tmp1##*.}";
            [[ "$_tmp2" != "$_tmp1" ]]  && { echo "$_tmp2"; }
            return 0;;
  esac
  return 1
}

function argsNumber () {
  ifHelpShow "$1" 'argsNumber "$@"
  Echos number of arguments.
  Wrapper for "$#" or "${#@}" which are equivalent.
  Verified by testing on bash 4.1.0(1):
      20140627 docsalvage
  Replaces:
      argsCount
  Revised:
      20140627 docsalvage'  && return
  #
  echo "$#"
  return 0
}

function isOption () {
  # isOption "$@"
  # Return true (0) if argument has 1 or more leading hyphens.
  # Example:
  #     isOption "$@"  && ...
  # Note:
  #   Cannot use ifHelpShow() here since cannot distinguish 'isOption --help'
  #   from 'isOption "$@"' where first argument in "$@" is '--help'
  # Revised:
  #     20140117 docsalvage
  # 
  # support both short and long options
  [[ "${1:0:1}" == "-" ]]  && return 0
  return 1
}

রিসোর্সেস


আমি বুঝতে পারি না - এটি ইতিমধ্যে এখানে ডেমোড করা হয়েছে যে সম্পূর্ণরূপে বহনযোগ্যভাবে কীভাবে করা যায় - আইএসএস ছাড়াই bash- এর চেয়ে আপাতদৃষ্টিতে সহজ ler এছাড়াও, কি ${#@}?
মাইকজার্ভ

এটি কেবল কার্যকারিতাটিকে পুনরায় ব্যবহারযোগ্য ফাংশনে প্যাকেজ করে। পুনরায়: $ {# @} ... অ্যারে এবং তাদের উপাদানগুলিতে ম্যানুপুলেটিংয়ের জন্য সম্পূর্ণ পরিবর্তনশীল স্বরলিপি প্রয়োজন $ {}} $ @ আর্গুমেন্টের 'অ্যারে'। $ {# @} আর্গুমেন্টের সংখ্যার জন্য বাশ সিনট্যাক্স।
ডকসালভেজার

না, $#আর্গুমেন্টের সংখ্যার জন্য বাক্য গঠন এবং এটি অন্য কোথাও ব্যবহৃত হয়।
মাইকজার্ভ

আপনি ঠিক বলেছেন যে "$ #" হ'ল "আর্গুমেন্টের সংখ্যা" এর জন্য বিস্তৃতভাবে নথিভুক্ত সিনট্যাক্স। তবে, আমি কেবল পুনরায় যাচাই করেছি যে "$ {# @}" সমতুল্য। অবস্থানগত আর্গুমেন্ট এবং অ্যারেগুলির মধ্যে পার্থক্য এবং সাদৃশ্য নিয়ে পরীক্ষা করার পরে আমি এটি দিয়ে ঘায়েল করেছি। পরে অ্যারে বাক্য গঠন থেকে আসে যা সংক্ষিপ্ত, সরল "$ #" সিনট্যাক্সের প্রতিশব্দ হিসাবে দৃশ্যত। "$ #" ব্যবহার করার জন্য আমি আরগস নাম্বার () টি পরিবর্তন এবং নথিভুক্ত করেছি। ধন্যবাদ!
ডকসালভেজার

${#@}বেশিরভাগ ক্ষেত্রে সমতুল্য নয় - দুর্ভাগ্যক্রমে, পসিক্স স্পেসে কোনও প্যারামিটার বিস্তারের ফলাফল বলা হয় $@বা $*অনির্ধারিত। এটি এতে কাজ করতে পারে bashতবে এটি একটি নির্ভরযোগ্য বৈশিষ্ট্য নয়, আমি অনুমান করি যা আমি বলার চেষ্টা করছি,,
মাইকজারভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.