একক বিবৃতি জন্য আইএফএস সেট করা


42

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

#check environment IFS value, it is space-tab-newline
printf "%s" "$IFS" | od -bc
0000000 040 011 012
             \t  \n
0000003
#invoke built-in with custom IFS
IFS=$'\n' read -r -d '' -a arr <<< "$str"
#environment IFS value remains unchanged as seen below
printf "%s" "$IFS" | od -bc
0000000 040 011 012
             \t  \n
0000003

#now attempt to set IFS for a single statement
IFS=$'\n' a=($str)
#BUT environment IFS value is overwritten as seen below
printf "%s" "$IFS" | od -bc
0000000 012
         \n
     0000001

উত্তর:


39

কিছু শেল (সহ bash):

IFS=: command eval 'p=($PATH)'

(এর সাথে bash, commandsh / POSIX অনুকরণে না থাকলে আপনি বাদ দিতে পারেন)। তবে সাবধান হন যে অব্যক্ত ভেরিয়েবলগুলি ব্যবহার করার সময় আপনার সাধারণত প্রয়োজনও হয় set -fএবং বেশিরভাগ শেলের মধ্যে এর জন্য স্থানীয় কোনও সুযোগ নেই।

Zsh সহ, আপনি এটি করতে পারেন:

(){ local IFS=:; p=($=PATH); }

$=PATHহ'ল শব্দ বিভাজনকে বাধ্য করা যা ডিফল্টরূপে না হয় zsh(পরিবর্তনশীল প্রসারণে গ্লোব্বিং হয় না তাই set -fশ এমুলেশন না থাকলে আপনার প্রয়োজন হয় না)।

(){...}(বা function {...}) বেনামে ফাংশন বলা হয় এবং সাধারণত স্থানীয় সুযোগ সেট করতে ব্যবহৃত হয়। ফাংশনগুলিতে স্থানীয় সুযোগকে সমর্থন করে এমন অন্যান্য শাঁসের সাথে আপনি এর সাথে কিছু করতে পারেন:

e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'

পসিক্স শেলগুলিতে ভেরিয়েবল এবং অপশনগুলির জন্য স্থানীয় সুযোগ প্রয়োগ করতে, আপনি https://github.com/stephane-chazelas/misc-scriptts/blob/master/locvar.sh এ প্রদত্ত ফাংশনগুলিও ব্যবহার করতে পারেন । তারপরে আপনি এটি ব্যবহার করতে পারেন:

. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'

( যাইহোক, এটি অন্যান্য শাঁসের মতো $PATHব্যতীত উপরের উপায়ে বিভক্ত হওয়া অবৈধ zsh, আইএফএস ক্ষেত্রের ডিলিমিটার, ক্ষেত্র বিভাজক নয়)।

IFS=$'\n' a=($str)

শুধুমাত্র দুটি কাজ, একের পর এক ঠিক ঠিক a=1 b=2

উপর ব্যাখ্যা একটি নোট var=value cmd:

ইন:

var=value cmd arg

শেলটি /path/to/cmdএকটি নতুন প্রক্রিয়াতে কার্যকর হয় এবং পাস cmdএবং argইন argv[]- var=valueইন করে envp[]। এটি আসলে কোনও ভেরিয়েবল অ্যাসাইনমেন্ট নয়, তবে এক্সিকিউটড কমান্ডে আরও বেশি পরিবেশগত পরিবর্তনশীল । বোর্ন বা কর্ন শেলের সাথে set -k, আপনি এটি লিখতেও পারেন cmd var=value arg

এখন, এটি বিল্টিনস বা ফাংশনগুলির ক্ষেত্রে প্রযোজ্য নয় যা কার্যকর হয় না । বোর্ন শেলের মধ্যে মধ্যে var=value some-builtin, varশেষ পর্যন্ত পরে মাত্র মতো সেট হওয়া, var=valueএকা। এর অর্থ উদাহরণস্বরূপ যে বিল্টিন রয়েছে var=value echo fooকি না echoতার উপর নির্ভর করে (যা কার্যকর নয়) এর আচরণের পরিবর্তিত হয় ।

পসিক্স এবং / অথবা kshপরিবর্তিত হয়েছে যে বর্ন আচরণ কেবল বিশেষ অন্তর্নির্মিত এক ধরণের বিল্টিনের জন্যই ঘটে । evalএকটি বিশেষ অন্তর্নির্মিত, readনা। বিশেষ বিশেষ বিল্টিনের জন্য, কেবল বিল্টিনের সঞ্চালনের জন্য var=value builtinসেট varকরে যা এটি বাহ্যিক কমান্ড চালিত হওয়ার সাথে অনুরূপ আচরণ করে।

commandকমান্ড মুছে ফেলার জন্য ব্যবহার করা যেতে পারে বিশেষ ব্যক্তিদের অ্যাট্রিবিউট বিশেষ builtins । পসিক্স যা উপেক্ষা করেছে তা হ'ল এটি evalএবং .বিল্টিনগুলির জন্য, এর অর্থ হ'ল শেলগুলি একটি ভেরিয়েবল স্ট্যাকটি প্রয়োগ করতে হবে (যদিও এটি কমান্ড localবা typesetসীমিত সীমাবদ্ধকরণ নির্দিষ্ট করে না ), কারণ আপনি এটি করতে পারেন:

a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a

অথবা এমনকি:

a=1 command eval myfunction

সঙ্গে myfunctionএকটি ফাংশন হচ্ছে ব্যবহার বা সেটিং $aএবং সম্ভাব্য কলিং command eval

এটি সত্যই একটি উপেক্ষা ছিল কারণ ksh(যা অনুমানটি বেশিরভাগ উপর ভিত্তি করে) এটি কার্যকর করে না (এবং এটিএন্ডটি kshএবং zshএখনও দেয় না) তবে আজকাল, এই দুটি ব্যতীত বেশিরভাগ শাঁস এটি বাস্তবায়ন করে। শেলগুলির মধ্যে আচরণগুলি বিভিন্ন রকম হয় যদিও:

a=0; a=1 command eval a=2; echo "$a"

যদিও। localএটি সমর্থন করে এমন শেলগুলি ব্যবহার করা স্থানীয় সুযোগ বাস্তবায়নের আরও নির্ভরযোগ্য উপায়।


Weirdly, IFS=: command eval …সেট IFSশুধুমাত্র চলাকালীন eval, যেমন ksh 93u মধ্যে ড্যাশ, pdksh এবং ব্যাশ এ, POSIX দ্বারা বাধ্যতামূলক, কিন্তু না। কেএসএসকে বিজোড়-অ-অনুপযুক্ত-এক-আউট হিসাবে দেখানো অস্বাভাবিক।
গিলস 21-29

12

কর্নিগান এবং পাইকের "দ্য ইউনিক্স প্রোগ্রামিং এনভায়রনমেন্ট" থেকে নেওয়া স্ট্যান্ডার্ড সেভ অ্যান্ড-রিস্টোর:

#!/bin/sh
old_IFS=$IFS
IFS="something_new"
some_program_or_builtin
IFS=${old_IFS}

2
আপনাকে ধন্যবাদ এবং +1। হ্যাঁ আমি এই বিকল্পটি সম্পর্কে সচেতন, তবে আমি কী বলতে চাই তা যদি আপনি জানেন তবে "ক্লিনার" বিকল্প আছে কিনা তা জানতে চাই
iruvar

আপনি এটি আধা-কলোন দিয়ে এক লাইনে জ্যাম করতে পারেন, তবে আমি মনে করি এটি পরিষ্কার নয়। আপনি যেটা প্রকাশ করতে চেয়েছিলেন তার প্রত্যেকটির বিশেষ সিনট্যাকটিক সমর্থন থাকলে এটি দুর্দান্ত হতে পারে তবে তারপরে আমাদের সম্ভবত কোডিংয়ের পরিবর্তে ছুতার বা স্যাম্পটিন শিখতে হবে;)
এমএসডাব্লু

9
$IFSএটি পূর্বে সেট না থাকলে সঠিকভাবে পুনরুদ্ধার করতে ব্যর্থ ।
স্টাফেন চেজেলাস

2
এটি যদি আনসেট না করা হয় তবে বাশ এটিকে $'\t\n'' 'এখানে যেমন ব্যাখ্যা করেছেন: উইকি.বাশ- হ্যাকারস.আর
স্যান্ট্যাক্স

2
@ ডেভিড, এটি হবে $' \t\n'। স্থান হিসাবে ব্যবহৃত হয় প্রথম হতে হবে "$*"। মনে রাখবেন যে সমস্ত বোর্নের মতো শেলগুলিতে এটি একই।
স্টাফেন চেজেলাস

8

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

main() {
  local IFS='/'

  # the rest goes here
}

main "$@"

6

এই আদেশের জন্য:

IFS=$'\n' a=($str)

একটি বিকল্প সমাধান রয়েছে: প্রথম অ্যাসাইনমেন্টটি ( IFS=$'\n') একটি কার্যকর করতে একটি কমান্ড দেওয়ার জন্য (একটি ফাংশন):

$ split(){ a=( $str ); }
$ IFS=$'\n' split

এটি বিভাজন কল করার জন্য পরিবেশে আইএফএস রাখবে, তবে বর্তমান পরিবেশে এটি ধরে রাখা হবে না।

এটি alওালের সর্বদা ঝুঁকিপূর্ণ ব্যবহার এড়িয়ে যায়।


Ksh93 এবং mksh এ, এবং পশিক্স মোডে থাকা অবস্থায় ব্যাশ এবং zsh এ এখনও পসিক্সের প্রয়োজন অনুসারে $IFSসেট হয়ে $'\n'যায়।
স্টাফেন চেজেলাস

4

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

#!/usr/bin/env bash
#
func() {
  # local IFS='\'

  local args=${@}
  echo -n "$FUNCNAME A"
  for ((i=0; i<${#args[@]}; i++)); do
    printf "[%s]: %s" "${i}" "${args[$i]}"
  done
  echo

  local f_args=( $(echo "${args[0]}") )
  echo -n "$FUNCNAME B"
  for ((i=0; i<${#f_args[@]}; i++)); do
    printf "[%s]: %s" "${i}" "${f_args[$i]}  "
  done
  echo
}

main() {
  local IFS='/'

  # the rest goes here
  local args=${@}
  echo -n "$FUNCNAME A"
  for ((i=0; i<${#args[@]}; i++)); do
    printf "[%s]: %s" "${i}" "${args[$i]}"
  done
  echo

  local m_args=( $(echo "${args[0]}") )
  echo -n "$FUNCNAME B"
  for ((i=0; i<${#m_args[@]}; i++)); do
    printf "[%s]: %s" "${i}" "${m_args[$i]}  "
  done
  echo

  func "${m_args[*]}"
}

main "$@"

এবং আউটপুট ...

main A[0]: ick/blick/flick
main B[0]: ick  [1]: blick  [2]: flick
func A[0]: ick/blick/flick
func B[0]: ick  [1]: blick  [2]: flick

আইএফএস যদি প্রধান () তে ঘোষিত হয় তবে এখনও ফানক () এ সুযোগটি না থাকলে, অ্যারেরটি ফানক () বি তে সঠিকভাবে পার্স করা হত না এবং ফানক () এর প্রথম লাইনটি কমকমেন্ট করা হয়েছে এবং আপনি এই আউটপুটটি পান:

main A[0]: ick/blick/flick
main B[0]: ick  [1]: blick  [2]: flick
func A[0]: ick/blick/flick
func B[0]: ick/blick/flick

আইএফএসের সুযোগের বাইরে চলে গেলে আপনার কী পাওয়া উচিত।

আইএমএইচওর থেকে আরও উন্নততর সমাধান হ'ল বিশ্ব / স্থানীয় স্তরে আইএফএসের উপর পরিবর্তন বা নির্ভর করা। পরিবর্তে, সেখানে নতুন আইএফএসের সাথে একটি নতুন শেল এবং ফিডল বানান। উদাহরণস্বরূপ, যদি আপনি নীচে ফান () কে মূল () কল করতে চান, তবে পিছনে স্ল্যাশ ফিল্ড বিভাজকের সাথে একটি স্ট্রিং হিসাবে অ্যারেটি পাস করা:

func $(IFS='\'; echo "${m_args[*]}")

... আইএফএসে পরিবর্তনটি ফানক () এর মধ্যে প্রতিফলিত হবে না। অ্যারেটি স্ট্রিং হিসাবে পাস করা হবে:

ick\blick\flick

... তবে ফানক () এর অভ্যন্তরে আইএফএসটি এখনও "/" থাকবে (মূলত () হিসাবে সেট করুন স্থানীয়ভাবে ফানক () এ পরিবর্তিত না হলে।

আইএফএসে পরিবর্তনগুলি বিচ্ছিন্ন করার বিষয়ে আরও তথ্য নীচের লিঙ্কগুলিতে দেখা যাবে:

আমি কীভাবে ব্যাশ অ্যারে ভেরিয়েবলকে নতুন লাইনের সাথে ডিলিমিট করা স্ট্রিংয়ে রূপান্তর করব?

আইএফএসের সাথে অ্যারেতে বাশ স্ট্রিং

সাধারণ শেল স্ক্রিপ্ট প্রোগ্রামিংয়ের জন্য ইঙ্গিতগুলি এবং টিপস - "সাব-শেলগুলির ব্যবহারটি লক্ষ করুন ..." দেখুন


সত্যিই আকর্ষণীয় ...
ইরুভর

"আইএফএসের সাথে অ্যারে টুথ স্ট্রিং" IFS=$'\n' declare -a astr=(...)নিখুঁত ধন্যবাদ!
অ্যাকোরিয়াস পাওয়ার

1

প্রশ্ন থেকে এই স্নিপেট:

IFS=$'\n' a=($str)

বাম থেকে ডানে দুটি পৃথক গ্লোবাল ভেরিয়েবল অ্যাসাইনমেন্ট হিসাবে ব্যাখ্যা করা হয়েছে এবং এর সমতুল্য:

IFS=$'\n'; a=($str)

অথবা

IFS=$'\n'
a=($str)

এটি উভয়কে ব্যাখ্যা করে যে কেন বিশ্বব্যাপী IFSসংশোধন করা হয়েছিল এবং কেন $strঅ্যারের উপাদানগুলিতে শব্দ-বিভাজন নতুন মানটি ব্যবহার করে সম্পাদিত হয়েছিল IFS

আপনি IFSএই জাতীয় পরিবর্তনের প্রভাব সীমাবদ্ধ করতে একটি সাবশেল ব্যবহার করতে প্রলুব্ধ হতে পারেন :

str="value 0:value 1"
a=( old values )
( # Following code runs in a subshell
 IFS=":"
 a=($str)
 printf 'Subshell IFS: %q\n' "${IFS}"
 echo "Subshell: a[0]='${a[0]}' a[1]='${a[1]}'"
)
printf 'Parent IFS: %q\n' "${IFS}"
echo "Parent: a[0]='${a[0]}' a[1]='${a[1]}'"

তবে আপনি দ্রুত লক্ষ্য করবেন যে পরিবর্তনটি aকেবল সাব-শেলের মধ্যেই সীমাবদ্ধ:

Subshell IFS: :
Subshell: a[0]='value 0' a[1]='value 1'
Parent IFS: $' \t\n'
Parent: a[0]='old' a[1]='values'

এর পরে, আপনি @ এমএসডাব্লু দ্বারা এই পূর্ববর্তী উত্তরটি থেকে সমাধানটি ব্যবহার করে আইএফএস সংরক্ষণ / পুনরুদ্ধার করার জন্য বা @ হেল্পারমেডোথের পরামর্শ অনুসারে কোনও local IFSফাংশনের অভ্যন্তরে কোনও চেষ্টা ও ব্যবহার করার প্রলোভন পাবেন। তবে খুব শীঘ্রই, আপনি খেয়াল করেছেন যে আপনি সব ধরণের সমস্যায় রয়েছেন, বিশেষত যদি আপনি কোনও লাইব্রেরি লেখক হন যাঁরা অন্যায় আচরণের স্ক্রিপ্টগুলিতে দুর্ব্যবহারের বিরুদ্ধে দৃ be় হওয়া দরকার:

  • যদি IFSপ্রাথমিকভাবে সেট না করা হয়?
  • যদি আমরা set -u(ওরফে set -o nounset) সাথে দৌড়াচ্ছি ?
  • যদি IFSকেবল পঠনযোগ্য মাধ্যমে তৈরি করা হয় declare -r IFS?
  • আমার যদি পুনরাবৃত্তি এবং trapঅ্যাসিনক্রোনাস এক্সিকিউশন (যেমন একটি হ্যান্ডলার হিসাবে ) এর সাথে কাজ করার জন্য সেভ / পুনরুদ্ধার ব্যবস্থা দরকার ?

আইএফএস সংরক্ষণ / পুনরুদ্ধার করবেন না দয়া করে। পরিবর্তে, অস্থায়ী পরিবর্তনগুলিতে আটকে থাকুন:

  • ভেরিয়েবল পরিবর্তনটি একটি একক কমান্ডের মধ্যে সীমাবদ্ধ করতে, অন্তর্নির্মিত বা ফাংশন আমন্ত্রণ, ব্যবহার করুন IFS="value" command

    • নির্দিষ্ট অক্ষরে বিভক্ত হয়ে একাধিক ভেরিয়েবলগুলি পড়তে ( :উদাহরণ হিসাবে নীচে ব্যবহৃত), ব্যবহার করুন:

      IFS=":" read -r var1 var2 <<< "$str"
    • একটি অ্যারের ব্যবহার পড়তে (পরিবর্তে এটি করুন array_var=( $str )):

      IFS=":" read -r -a array_var <<< "$str"
  • ভেরিয়েবলটি সাব-শেলের পরিবর্তনের প্রভাব সীমিত করুন।

    • অ্যারের উপাদানগুলি কমা দ্বারা পৃথক করে আউটপুট করতে:

      (IFS=","; echo "${array[*]}")
    • এটি একটি স্ট্রিংয়ে ক্যাপচার করতে:

      csv="$(IFS=","; echo "${array[*]}")"

0

সবচেয়ে সোজা ফরোয়ার্ড সলিউশনটি হ'ল $IFSআসলটির অনুলিপি নেওয়া যেমন এমএসডাব্লু এর উত্তর। তবে, এই সমাধানটি কোনও আনসেট IFSএবং IFSখালি স্ট্রিংয়ের সমান একটি সেটের মধ্যে পার্থক্য করে না , যা অনেক অ্যাপ্লিকেশনের জন্য গুরুত্বপূর্ণ। এখানে আরও একটি সাধারণ সমাধান যা এই পার্থক্যটি ধারণ করে:

# Functions taking care of IFS
set_IFS(){
    if [ -z "${IFS+x}" ]; then
        IFS_ori="__unset__"
    else
        IFS_ori="$IFS"
    fi
    IFS="$1"
}
reset_IFS(){
    if [ "${IFS_ori}" == "__unset__" ]; then
        unset IFS
    else
        IFS="${IFS_ori}"
    fi
}

# Example of use
set_IFS "something_new"
some_program_or_builtin
reset_IFS
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.