কিভাবে আমরা একটি ভেরিয়েবলে সঞ্চিত কমান্ড চালাতে পারি?


35
$ ls -l /tmp/test/my\ dir/
total 0

আমি ভাবছিলাম যে উপরের কমান্ডটি চালানোর জন্য নিম্নলিখিত উপায়গুলি ব্যর্থ বা সফল?

$ abc='ls -l "/tmp/test/my dir"'

$ $abc
ls: cannot access '"/tmp/test/my': No such file or directory
ls: cannot access 'dir"': No such file or directory

$ "$abc"
bash: ls -l "/tmp/test/my dir": No such file or directory

$ bash -c $abc
'my dir'

$ bash -c "$abc"
total 0

$ eval $abc
total 0

$ eval "$abc"
total 0


উত্তর:


54

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


কেন এটি ব্যর্থ হয়

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

প্রশ্নে উপস্থাপিত মামলাগুলি:

$ abc='ls -l "/tmp/test/my dir"'

এখানে $abcবিভক্ত হয়ে lsদুটি যুক্তি "/tmp/test/myএবং dir"(প্রথমটির দ্বিতীয় এবং দ্বিতীয়টির পিছনে উদ্ধৃতি সহ) পাওয়া যায়:

$ $abc
ls: cannot access '"/tmp/test/my': No such file or directory
ls: cannot access 'dir"': No such file or directory

এখানে, সম্প্রসারণের উদ্ধৃতি দেওয়া হয়েছে, সুতরাং এটি একটি শব্দ হিসাবে রাখা হয়েছে। শেলটি একটি প্রোগ্রাম হিসাবে সন্ধান করার চেষ্টা করে ls -l "/tmp/test/my dir", স্থান এবং কোট অন্তর্ভুক্ত।

$ "$abc"
bash: ls -l "/tmp/test/my dir": No such file or directory

এবং এখানে, কেবল প্রথম শব্দটি বা $abcতর্ক হিসাবে বিবেচিত হয় -c, সুতরাং বাশ কেবলমাত্র lsবর্তমান ডিরেক্টরিতে চালিত হয়। অন্য কথায় ব্যাশ আর্গুমেন্ট আছে, এবং পূরণ করতে ব্যবহার করা হয় $0, $1ইত্যাদি

$ bash -c $abc
'my dir'

সহ bash -c "$abc", এবং eval "$abc", একটি অতিরিক্ত শেল প্রসেসিং পদক্ষেপ রয়েছে, যা কোটগুলি কার্যকর করে তোলে, তবে সমস্ত শেল বিস্তৃতি আবার প্রক্রিয়াজাতকরণের কারণও বটে , সুতরাং ব্যবহারকারী-সরবরাহিত ডেটা থেকে ঘটনাক্রমে কমান্ড সম্প্রসারণের ঝুঁকি রয়েছে, যদি আপনি না হন উদ্ধৃতি সম্পর্কে সাবধান।


এটি করার আরও ভাল উপায়

কমান্ড সংরক্ষণের আরও দুটি ভাল উপায় হ'ল ক) পরিবর্তে একটি ফাংশন ব্যবহার করুন, খ) একটি অ্যারে ভেরিয়েবল (বা অবস্থানিক পরামিতি) ব্যবহার করুন।

একটি ফাংশন ব্যবহার করে:

কেবল কমান্ডটি ভিতরে ভিতরে একটি ক্রিয়াকলাপ ঘোষণা করুন, এবং এটি একটি আদেশ হিসাবে যেন ফাংশনটি চালান। ফাংশনটির মধ্যে কমান্ডগুলির বিস্তৃতি কেবলমাত্র যখন কমান্ডটি চালিত হয় তখনই প্রক্রিয়া করা হয়, যখন এটি সংজ্ঞায়িত হয় না, এবং আপনাকে পৃথক কমান্ডের উদ্ধৃতি দেওয়ার প্রয়োজন হয় না।

# define it
myls() {
    ls -l "/tmp/test/my dir"
}

# run it
myls

একটি অ্যারে ব্যবহার:

অ্যারেগুলি একাধিক-শব্দ ভেরিয়েবল তৈরি করতে দেয় যেখানে পৃথক শব্দের মধ্যে সাদা স্থান থাকে। এখানে, পৃথক শব্দগুলি পৃথক অ্যারে উপাদান হিসাবে সংরক্ষণ করা হয় এবং "${array[@]}"প্রসারণ প্রতিটি উপাদানকে পৃথক শেল শব্দ হিসাবে প্রসারিত করে:

# define the array
mycmd=(ls -l "/tmp/test/my dir")

# run the command
"${mycmd[@]}"

বাক্য গঠনটি খানিকটা ভয়ঙ্কর, তবে অ্যারে আপনাকে কমান্ড লাইন টুকরো টুকরো করে তৈরি করতে দেয়। উদাহরণ স্বরূপ:

mycmd=(ls)               # initial command
if [ "$want_detail" = 1 ]; then
    mycmd+=(-l)          # optional flag
fi
mycmd+=("$targetdir")    # the filename

"${mycmd[@]}"

অথবা কমান্ড লাইনের অংশগুলি স্থির রাখুন এবং অ্যারে ব্যবহার করুন এটির একটি অংশ, বিকল্প বা ফাইলের নাম পূরণ করুন:

options=(-x -v)
files=(file1 "file name with whitespace")
target=/somedir

transmutate "${options[@]}" "${files[@]}" "$target"

অ্যারেগুলির ক্ষতিটি হ'ল এগুলি কোনও স্ট্যান্ডার্ড বৈশিষ্ট্য নয়, সুতরাং সরল পসিক্স শেলগুলি (যেমন ডিবিয়ান / উবুন্টুতে dashডিফল্ট /bin/sh) তাদের সমর্থন করে না (তবে নীচে দেখুন)। বাশ, ksh এবং zsh করবেন, তবে এটি সম্ভবত আপনার সিস্টেমে কিছু শেল রয়েছে যা অ্যারে সমর্থন করে।

ব্যবহার "$@"

নামযুক্ত অ্যারেগুলির কোনও সমর্থন ছাড়াই শেলগুলিতে, "$@"কমান্ডের আর্গুমেন্টগুলি ধরে রাখার জন্য এখনও কেউ অবস্থানীয় পরামিতিগুলি (সিউডো-অ্যারে ) ব্যবহার করতে পারে।

নিম্নলিখিতটি পোর্টেবল স্ক্রিপ্ট বিটগুলি হওয়া উচিত যা পূর্ববর্তী বিভাগে কোড বিটের সমতুল্য হয়। অ্যারের সাথে প্রতিস্থাপিত হয় "$@", অবস্থানগত পরামিতিগুলির তালিকা। সেটিংটি "$@"সম্পন্ন করা হয় setএবং চারপাশে ডাবল উদ্ধৃতিগুলি "$@"গুরুত্বপূর্ণ (এগুলি তালিকার উপাদানগুলিকে স্বতন্ত্রভাবে উদ্ধৃত করার কারণ হিসাবে)।

প্রথমে একটি কমান্ড সংরক্ষণ করে আর্গুমেন্ট যুক্ত করে "$@"এটি চালানো:

set -- ls -l "/tmp/test/my dir"
"$@"

শর্তাধীনভাবে কমান্ডের জন্য কমান্ড লাইন বিকল্পের অংশগুলি সেট করা:

set -- ls
if [ "$want_detail" = 1 ]; then
    set -- "$@" -l
fi
set -- "$@" "$targetdir"

"$@"

শুধুমাত্র "$@"বিকল্প এবং অপারেশনগুলির জন্য ব্যবহার :

set -- -x -v
set -- "$@" file1 "file name with whitespace"
set -- "$@" /somedir

transmutate "$@"

(অবশ্যই, "$@"সাধারণত স্ক্রিপ্টে যুক্তি দিয়ে ভরা থাকে, তাই আপনাকে পুনরায় ধারণা দেওয়ার আগে সেগুলি কোথাও সংরক্ষণ করতে হবে "$@"))


সাবধান eval!

হিসাবে evalপ্রবর্তন উদ্ধৃতি ও সম্প্রসারণ প্রক্রিয়াকরণ একটি অতিরিক্ত স্তর, আপনি ব্যবহারকারীর ইনপুট সঙ্গে সতর্কতা অবলম্বন করা প্রয়োজন। উদাহরণস্বরূপ, এটি ততক্ষণ কাজ করে যতক্ষণ না ব্যবহারকারী কোনও একক উদ্ধৃতিতে টাইপ না করে:

read -r filename
cmd="ls -l '$filename'"
eval "$cmd";

তবে যদি তারা ইনপুট দেয় '$(uname)'.txt, আপনার স্ক্রিপ্টটি আনন্দের সাথে কমান্ড প্রতিস্থাপন চালায়।

অ্যারে সহ একটি সংস্করণ এ থেকে অনাক্রম্য যেহেতু শব্দগুলি পুরো সময়ের জন্য পৃথক রাখা হয়, সুতরাং সামগ্রীর সামগ্রীর জন্য কোনও উদ্ধৃতি বা অন্য প্রক্রিয়াজাতকরণ নেই filename

read -r filename
cmd=(ls -ld -- "$filename")
"${cmd[@]}"

তথ্যসূত্র


2
আপনি Eval করে জিনিস উদ্ধৃত দেখতে পারেন cmd="ls -l $(printf "%q" "$filename")"। সুন্দর নয়, তবে ব্যবহারকারী যদি এটি ব্যবহারে মৃত হয়ে থাকে তবে evalএটি সহায়তা করে। অনুরূপ জিনিস, যেমন ssh foohost "ls -l $(printf "%q" "$filename")", বা এই প্রশ্নের স্রোতে হলেও, কমান্ডটি প্রেরণের জন্য এটি খুব দরকারী ssh foohost "$cmd"
প্যাট্রিক

সরাসরি সম্পর্কিত নয়, তবে আপনি কি ডিরেক্টরিটি হার্ড-কোড করেছেন? সেক্ষেত্রে আপনি হয়ত উপনামটি দেখতে চাইবেন। এর মতো কিছু: $ alias abc='ls -l "/tmp/test/my dir"'
বুনি আশা করছেন

4

(নন-তুচ্ছ) কমান্ডটি চালানোর নিরাপদতম উপায় eval। আপনি কমান্ড লাইনে যেমন কমান্ডটি লিখেছিলেন তেমন লিখতে পারেন এবং এটি ঠিক সম্পাদিত হয় যেন আপনি সবেমাত্র এটি প্রবেশ করিয়েছেন। তবে আপনাকে সবকিছু উদ্ধৃত করতে হবে।

সাধারণ কেস:

abc='ls -l "/tmp/test/my dir"'
eval "$abc"

এত সহজ ঘটনা নয়:

# command: awk '! a[$0]++ { print "foo: " $0; }' inputfile
abc='awk '\''! a[$0]++ { print "foo: " $0; }'\'' inputfile'
eval "$abc"

3

দ্বিতীয় উদ্ধৃতি সাইন কমান্ড বিরতি।

আমি যখন চালাচ্ছি:

abc="ls -l '/home/wattana/Desktop'"
$abc

এটি আমাকে একটি ত্রুটি দিয়েছে।

তবে আমি যখন দৌড়ে যাই

abc="ls -l /home/wattana/Desktop"
$abc

কোনও ত্রুটি নেই

এটিকে ঠিক করার কোনও উপায় নেই (আমার জন্য) তবে ডিরেক্টরিটির নামে স্থান না পেয়ে আপনি ত্রুটিটি এড়াতে পারেন।

এই উত্তরে বলা হয়েছে যে এভাল কমান্ডটি এটি ঠিক করতে ব্যবহার করা যেতে পারে তবে এটি আমার পক্ষে কার্যকর হয় না :(


1
হ্যাঁ, এটি ততক্ষণ কাজ করে যা এম্বেড থাকা ফাঁকা জায়গাগুলির (বা গ্লোব অক্ষরযুক্ত) ফাইল ফাইলগুলির প্রয়োজন নেই।
ইলকচাচু

0

এটি যদি কাজ করে না '', তবে আপনার ব্যবহার করা উচিত ``:

abc=`ls -l /tmp/test/my\ dir/`

উন্নততর উপায় আপডেট করুন:

abc=$(ls -l /tmp/test/my\ dir/)

এটি কমান্ডের ফলাফলটি একটি ভেরিয়েবলে সঞ্চয় করে। ওপি (আশ্চর্যজনকভাবে) কমান্ডটি একটি ভেরিয়েবলের মধ্যে সংরক্ষণ করতে চায়। ওহ, এবং আপনার $( command... )ব্যাকটিক্সের পরিবর্তে সত্যিই ব্যবহার শুরু করা উচিত ।
রোয়াইমা

স্পষ্টতা এবং টিপস জন্য আপনাকে অনেক ধন্যবাদ!
বেলুন

0

পাইথন 3 ওয়ান-লাইনার সম্পর্কে কীভাবে?

bash-4.4# pwd
/home
bash-4.4# CUSTOM="ls -altr"
bash-4.4# python3 -c "import subprocess; subprocess.call(\"$CUSTOM\", shell=True)"
total 8
drwxr-xr-x    2 root     root          4096 Mar  4  2019 .
drwxr-xr-x    1 root     root          4096 Nov 27 16:42 ..
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.