কার্যকর করার সময় ভেরিয়েবলগুলি উদ্ধৃত করা উচিত?


18

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

তবে নীচের মত একটি ক্রিয়া বিবেচনা করুন:

run_this(){
    $@
}

করা উচিত $@সেখানে বা না উদ্ধৃত করা? আমি এটির সাথে কিছুটা সময় খেলেছি এবং কোটের অভাবজনিত সমস্যার কারণে এমন কোনও মামলা খুঁজে পাইনি। অন্যদিকে, উদ্ধৃতিগুলি ব্যবহার করে তা উদ্ধৃত ভেরিয়েবল হিসাবে ফাঁকা স্থান যুক্ত কমান্ডটি পাস করার সময় ভেঙে দেয়:

#!/usr/bin/sh
set -x
run_this(){
    $@
}
run_that(){
    "$@"
}
comm="ls -l"
run_this "$comm"
run_that "$comm"

উপরে স্ক্রিপ্ট চালানো ফিরে আসে:

$ a.sh
+ comm='ls -l'
+ run_this 'ls -l'
+ ls -l
total 8
-rw-r--r-- 1 terdon users  0 Dec 22 12:58 da
-rw-r--r-- 1 terdon users 45 Dec 22 13:33 file
-rw-r--r-- 1 terdon users 43 Dec 22 12:38 file~
+ run_that 'ls -l'
+ 'ls -l'
/home/terdon/scripts/a.sh: line 7: ls -l: command not found

আমি এটির run_that $commপরিবর্তে যদি আমি ব্যবহার করতে পারি run_that "$comm"তবে এটি যেহেতু run_this(অব্যক্ত) ফাংশন দুটিয়ের সাথেই কাজ করে, এটি নিরাপদ বাজির মতো বলে মনে হচ্ছে।

সুতরাং, $@একটি ফাংশন ব্যবহারের নির্দিষ্ট ক্ষেত্রে যার কাজ হ'ল $@কমান্ড হিসাবে চালানো , $@তাকে উদ্ধৃত করা উচিত ? দয়া করে ব্যাখ্যা করুন কেন এটি / উদ্ধৃত করা উচিত নয় এবং ডেটার একটি উদাহরণ দিন যা এটি ভেঙে দিতে পারে।


6
run_thatএর আচরণ অবশ্যই আমি প্রত্যাশা করেছিলাম (কমান্ডের পথে কোনও জায়গা থাকলে কী হবে?) আপনি যদি অন্য আচরণটি চান, অবশ্যই আপনি কল- সাইটটিতে এটিকে উদ্ধৃত করবেন যেখানে আপনি জানেন যে ডেটা কী? আমি এই ফাংশনটি হিসাবে কল করতে আশা করব run_that ls -l, যা উভয় সংস্করণে একই কাজ করে। এমন কোনও মামলা রয়েছে যা আপনাকে আলাদাভাবে প্রত্যাশা করে?
মাইকেল হোমার

আমার সম্পাদন করা এখানে এই অনুরোধ জানানো @MichaelHomer আমি unix.stackexchange.com/a/250985/70524
muru

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

শেলগুলি এখনও একটি অ্যারেতে কেবল কমান্ড পূরণ এবং এটি দিয়ে কার্যকর করার পরিবর্তে ফাংশন সমর্থন করার একটি কারণ রয়েছে ${mycmd[@]}
চেপনার

উত্তর:


20

কমান্ডটি ফাংশনটিতে কীভাবে পাঠানো হয় তার মধ্যে সমস্যাটি রয়েছে:

$ run_this ls -l Untitled\ Document.pdf 
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_that ls -l Untitled\ Document.pdf 
-rw------- 1 muru muru 33879 Dec 20 11:09 Untitled Document.pdf

"$@"আপনার run_thisফাংশনটি সাধারণত লিখিত কমান্ডের সাথে উপস্থাপিত হয় এমন ক্ষেত্রে ব্যবহার করা উচিত । run_thisজাহান্নামের উদ্ধৃতি দেয়:

$ run_this 'ls -l Untitled\ Document.pdf'
ls: cannot access Untitled\: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_this 'ls -l "Untitled\ Document.pdf"'
ls: cannot access "Untitled\: No such file or directory
ls: cannot access Document.pdf": No such file or directory
$ run_this 'ls -l Untitled Document.pdf'
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory
$ run_this 'ls -l' 'Untitled Document.pdf'
ls: cannot access Untitled: No such file or directory
ls: cannot access Document.pdf: No such file or directory

আমি কীভাবে ফাঁকা জায়গাগুলির সাথে একটি ফাইল নাম পাস করব তা আমি নিশ্চিত নই run_this


1
এটি আসলেই আপনার সম্পাদনা যা এটিকে অনুরোধ করেছিল। কোনও কারণে শূন্যস্থান সহ কোনও ফাইলের নাম পরীক্ষা করার জন্য আমার কাছে এটি ঘটেনি। আমি কেন জানি না তার একেবারে ধারণা নেই তবে আপনি সেখানে যান। আপনি অবশ্যই একেবারে সঠিক, আমি এটির সাথে run_thisকোনওভাবেই সঠিকভাবে করার কোনও উপায় দেখছি না ।
টেরডন

@ স্টারডন উদ্ধৃতি এতটা অভ্যাস হয়ে গেছে যে আমি ধরে $@নিয়েছিলাম যে আপনি দুর্ঘটনাক্রমে বিনা প্রশ্নে ছেড়ে চলে যাবেন d আমার একটা উদাহরণ রাখা উচিত ছিল। : ডি
মুড়ু

2
নাহ, এটি সত্যিই এতটা অভ্যাস যে আমি এটি পরীক্ষা করেছি (ভুলভাবে) এবং এই সিদ্ধান্তে পৌঁছেছি যে "হু, সম্ভবত এটির কোনও উদ্ধৃতি দরকার নেই"। একটি প্রক্রিয়া যা সাধারণত ব্রেইনফার্ট হিসাবে পরিচিত।
টেরডন

1
স্পেস সহ আপনি কোনও ফাইলের নাম পাস করতে পারবেন না run_thisবাশ এফএকিউ 050-তে আলোচিত স্ট্রিংগুলিতে জটিল কমান্ডগুলি স্টাফিংয়ের মাধ্যমে আপনি মূলত একই সমস্যাটি পান ।
এটান রিজনার

9

এটি হয়:

interpret_this_shell_code() {
  eval "$1"
}

বা:

interpret_the_shell_code_resulting_from_the_concatenation_of_those_strings_with_spaces() {
  eval "$@"
}

বা:

execute_this_simple_command_with_these_arguments() {
  "$@"
}

কিন্তু:

execute_the_simple_command_with_the_arguments_resulting_from_split+glob_applied_to_these_strings() {
  $@
}

খুব একটা বুদ্ধি নেই।

আপনি যদি ls -lকমান্ডটি চালাতে চান ( lsকমান্ডটি যুক্তি হিসাবে lsএবং নয় -l), আপনি করতেন:

interpret_this_shell_code '"ls -l"'
execute_this_simple_command_with_these_arguments 'ls -l'

তবে (সম্ভবত আরও বেশি), এটি যুক্তি lsসহ lsএবং -lযুক্তি হিসাবে কমান্ড , আপনি চালাতে চান:

interpret_this_shell_code 'ls -l'
execute_this_simple_command_with_these_arguments ls -l

এখন, যদি আপনি চালিত করতে চান এমন একটি সাধারণ কমান্ডের চেয়ে বেশি, আপনি যদি ভেরিয়েবল অ্যাসাইনমেন্ট, পুনর্নির্দেশ, পাইপ ... করতে চান তবে কেবলমাত্র এটি interpret_this_shell_codeকরবে:

interpret_this_shell_code 'ls -l 2> /dev/null'

যদিও অবশ্যই আপনি সর্বদা করতে পারেন:

execute_this_simple_command_with_these_arguments eval '
  ls -l 2> /dev/null'

5

ব্যাশ / ksh / zsh দৃষ্টিকোণ থেকে এটা এ খুঁজছি, $*এবং $@সাধারণ অ্যারে সম্প্রসারণ একটি বিশেষ ক্ষেত্রে হয়। অ্যারের বিস্তৃতি সাধারণ পরিবর্তনশীল বিস্তারের মতো নয়:

$ a=("a b c" "d e" f)
$ printf ' -> %s\n' "${a[*]}"
 -> a b c d e f
$ printf ' -> %s\n' "${a[@]}"
-> a b c
-> d e
-> f
$ printf ' -> %s\n' ${a[*]}
 -> a
 -> b
 -> c
 -> d
 -> e
 -> f
$ printf ' -> %s\n' ${a[@]}
 -> a
 -> b
 -> c
 -> d
 -> e
 -> f

সঙ্গে $*/ ${a[*]}প্রসারণও আপনি অ্যারের প্রথম মান সঙ্গে যোগ দেন পেতে IFS-যেটা ডিফল্ট-মধ্যে এক দৈত্য স্ট্রিং-এর দ্বারা স্থান। আপনি যদি এটিকে উদ্ধৃতি না দিয়ে থাকেন তবে এটি স্বাভাবিক স্ট্রিংয়ের মতো বিভক্ত হয়।

সঙ্গে $@/ ${a[@]}প্রসারণও, ব্যবহার কিনা উপর নির্ভর করে $@/ ${a[@]}সম্প্রসারণ বা উদ্ধৃত নয়:

  1. যদি এটি উদ্ধৃত হয় ( "$@"বা "${a[@]}"), আপনি "$1" "$2" "$3" #... বা এর সমতুল্য পাবেন "${a[1]}" "${a[2]}" "${a[3]}" # ...
  2. যদি এটা উদ্ধৃত করা হয় না ( $@বা ${a[@]}) আপনি সমতুল্য পেতে $1 $2 $3 #... বা${a[1]} ${a[2]} ${a[3]} # ...

মোড়ানো কমান্ডগুলির জন্য, আপনি অবশ্যই অবশ্যই উদ্ধৃত @ সম্প্রসারণটি চান (১)।


বাশ (এবং বাশ-জাতীয়) অ্যারে সম্পর্কিত আরও ভাল তথ্য: https://lukeshu.com/blog/bash-arrays.html


1
ঠিক বুঝতে পেরেছি যে আমি লুকের সাথে শুরু হওয়া একটি লিঙ্কটি উল্লেখ করছি, যখন ভাদার মুখোশ পরেছিলাম। এই পোস্টটি দিয়ে বলটি শক্তিশালী।
PSkocik

4

যেহেতু আপনি দ্বিগুণ উদ্ধৃতি করবেন না $@, আপনি আপনার ফাংশনটিতে যে লিঙ্কটি দিয়েছেন তাতে সমস্ত গ্লোববিং ইস্যু ছেড়ে গেছেন ।

আপনি কীভাবে একটি কমান্ড চালাতে পারেন *? আপনি এটি দিয়ে এটি করতে পারবেন না run_this:

$ ls
1 2
$ run_this '*'
dash: 2: 1: not found
$ run_that '*'
dash: 3: *: not found

এবং আপনি দেখুন, ত্রুটি দেখা দেওয়ার পরেও run_thatআপনাকে আরও অর্থপূর্ণ বার্তা দিয়েছে।

$@স্বতন্ত্র শব্দের প্রসারিত করার একমাত্র উপায় হ'ল এটি দ্বিগুণ es আপনি যদি এটি একটি কমান্ড হিসাবে চালাতে চান তবে আপনার কমান্ডটি পাস করা উচিত এবং এটি পৃথক শব্দের হিসাবে পরামিতিগুলি প্রবেশ করবে। আপনি যে কাজটি কলারের দিকে করেছিলেন তা আপনার ফাংশনের ভিতরে নয়।

$ cmd=ls
$ param1=-l
$ run_that "$cmd" "$param1"
total 0
-rw-r--r-- 1 cuonglm cuonglm 0 Dec 23 17:33 1
-rw-r--r-- 1 cuonglm cuonglm 0 Dec 23 17:33 2

একটি ভাল পছন্দ। অথবা যদি আপনার শেল সমর্থন করে:

$ cmd=(ls -l)
$ run_that "${cmd[@]}"
total 0
-rw-r--r-- 1 cuonglm cuonglm 0 Dec 23 17:33 1
-rw-r--r-- 1 cuonglm cuonglm 0 Dec 23 17:33 2

এমনকি শেল যখন বিন্যাসকে মোটেই সমর্থন করে না, তবুও আপনি এটি ব্যবহার করে খেলতে"$@" পারেন ।


3

ভেরিয়েবলগুলি কার্যকর bashকরা একটি ব্যর্থতা-প্রবণ কৌশল। এমন কোনও run_thisফাংশন লেখা সহজ নয় যা সঠিকভাবে সমস্ত প্রান্তের কেসগুলি পরিচালনা করে, যেমন:

  • পাইপলাইন (যেমন ls | grep filename)
  • ইনপুট / আউটপুট পুনর্নির্দেশগুলি (যেমন ls > /dev/null)
  • if whileইত্যাদি শেল স্টেটমেন্ট

আপনি যা করতে চান তা যদি কোড পুনরাবৃত্তি এড়ানো হয় তবে আপনি ফাংশনগুলি ব্যবহার করে ভাল। উদাহরণস্বরূপ, পরিবর্তে:

run_this(){
    "$@"
}
command="ls -l"
...
run_this "$command"

আপনার লেখা উচিত

command() {
    ls -l
}
...
command

যদি কমান্ডগুলি কেবল রান সময়ে উপলভ্য থাকে তবে আপনার ব্যবহার করা উচিত eval, যা বিশেষত সমস্ত quirks পরিচালনা করতে ডিজাইন করা হয়েছে যা run_thisব্যর্থ হয়ে যাবে:

command="ls -l | grep filename > /dev/null"
...
eval "$command"

লক্ষ্য করুন evalহয় পরিচিত নিরাপত্তা সমস্যার জন্য, কিন্তু যদি তোমাদের কাছে অবিশ্বস্ত সূত্র থেকে ভেরিয়েবল পাস run_this, আপনি শুধু পাশাপাশি নির্বিচারে কোড এক্সিকিউশন মুখোমুখি হবে।


1

সিদ্ধান্ত আপনার. আপনি যদি $@এর মানগুলির কোনও উদ্ধৃতি না করেন তবে অতিরিক্ত সম্প্রসারণ এবং ব্যাখ্যার মধ্য দিয়ে যায়। আপনি যদি এটিকে উদ্ধৃত করেন তবে কার্যকারিতাটি পাস করা সমস্ত আর্গুমেন্ট তার সম্প্রসারণ ভারব্যাটিমে পুনরুত্পাদন করা হয়। আপনি যে &>|কোনও উপায়ে নিজেই যুক্তিগুলি পার্স না করে কোনওভাবেই শেল সিনট্যাক্স টোকেনকে নির্ভরযোগ্যভাবে পরিচালনা করতে সক্ষম হবেন না - এবং তাই আপনার ফাংশনগুলির মধ্যে একটির হস্তান্তর করার আরও যুক্তিসঙ্গত পছন্দগুলি আপনি রেখে গেছেন:

  1. একক সাধারণ কমান্ডের সাথে কার্যকরভাবে ব্যবহৃত শব্দগুলি "$@"

... অথবা ...

  1. আপনার আর্গুমেন্টগুলির আরও বিস্তৃত এবং ব্যাখ্যাযুক্ত সংস্করণ যা কেবল তখনই সাধারণ কমান্ড হিসাবে একসাথে প্রয়োগ করা হয় $@

কোনও উদ্দেশ্যই ভুল নয় যদি তা ইচ্ছাকৃত হয় এবং যদি আপনি যা পছন্দ করেন তার প্রভাবগুলি ভালভাবে বোঝা যায়। উভয় পদ্ধতির একে অপরের তুলনায় সুবিধাগুলি রয়েছে, যদিও দ্বিতীয়টির সুবিধাগুলি খুব কমই সম্ভবত বিশেষভাবে কার্যকর হবে। এখনও ...

(run_this(){ $@; }; IFS=@ run_this 'ls@-dl@/tmp')

drwxrwxrwt 22 root root 660 Dec 28 19:58 /tmp

... এটি অকেজো নয় , খুব সম্ভবত খুব বেশি ব্যবহারের সম্ভাবনা রয়েছে । এবং একটি bashশেলের মধ্যে, কারণ bashডিফল্টরূপে তার পরিবেশের একটি পরিবর্তনশীল সংজ্ঞা আটকে থাকে না এমনকি যখন বলা হয় যে একটি বিশেষ বিল্টিনের কমান্ড-লাইনে বা কোনও ক্রিয়াকলাপের কাছে সংজ্ঞাটি চাপানো হয়, এর জন্য বিশ্বব্যাপী মানটি $IFSপ্রভাবিত হয় না এবং এর ঘোষণাটি স্থানীয় হয় শুধু run_this()কল করতে

একইভাবে:

(run_this(){ $@; }; set -f; run_this ls -l \*)

ls: cannot access *: No such file or directory

... গ্লোববিংও কনফিগারযোগ্য। উক্তিগুলি একটি উদ্দেশ্য পরিবেশন করে - এগুলি কোনও কিছুর জন্য নয়। এগুলি ছাড়া শেল সম্প্রসারণ অতিরিক্ত ব্যাখ্যার মধ্য দিয়ে যায় - কনফিগারযোগ্য ব্যাখ্যা interpretation এটি ব্যবহৃত হত - বেশ কয়েকটি পুরানো শাঁসের সাথে - যা বিশ্বব্যাপী সমস্ত ইনপুটগুলিতে প্রয়োগ $IFSহয়েছিল , এবং কেবল বিস্তৃতি নয়। প্রকৃতপক্ষে, বলেছিল শেলগুলি এর মতোই আচরণ করে যা তারা সমস্ত ইনপুট শব্দগুলির মানকে ভেঙে দেয় । এবং তাই, আপনি যা সন্ধান করছেন তা যদি খুব পুরানো শেল আচরণ হয় তবে আপনার ব্যবহার করা উচিতrun_this()$IFSrun_this()

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

(run_that(){ "$@"; }; IFS=l run_that 'ls' '-ld' '/tmp')

drwxrwxrwt 22 root root 660 Dec 28 19:58 /tmp

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

তাই:

'for' f in ...
 do   :
 done

bash: for: command not found
bash:  do: unexpected token 'do'
bash:  do: unexpected token 'done'

তুমি যে কাজ করতে উভয় সঙ্গে সক্ষম হবেন run_this()বা run_that()

কিন্তু ফাংশন নাম, বা $PATH'ঘ কমান্ড, অথবা builtins শুধু জরিমানা উদ্ধৃত বা unquoted চালানো হবে, এবং যে ঠিক কীভাবে তা run_this()এবং run_that()কাজ প্রথম স্থানে। আপনি এগুলির কোনওটির সাথে দরকারী কিছু করতে সক্ষম হবেন না $<>|&(){}। সংক্ষিপ্ত eval, হয়।

(run_that(){ "$@"; }; run_that eval printf '"%s\n"' '"$@"')

eval
printf
"%s\n"
"$@"

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

(run_that(){ "$@";}; echo hey | run_that cat)

hey

পাইপটি খোলার সাথে সাথে আমি সহজেই এখানে <ইনডপুট বা >আউটপুট পুনর্নির্দেশ করতে পারতাম ।

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

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