একটি শেল স্ক্রিপ্টে একটি চলকটিতে একটি কমান্ড কীভাবে সংরক্ষণ করবেন?


113

আমি পরবর্তীতে একটি চলকটিতে ব্যবহার করার জন্য একটি কমান্ড সংরক্ষণ করতে চাই (কমান্ডের আউটপুট নয়, কমান্ড নিজেই)

আমার নিম্নরূপে একটি সহজ স্ক্রিপ্ট রয়েছে:

command="ls";
echo "Command: $command"; #Output is: Command: ls

b=`$command`;
echo $b; #Output is: public_html REV test... (command worked successfully)

যাইহোক, আমি যখন আরও কিছু জটিল কিছু চেষ্টা করি তখন তা ব্যর্থ হয়। উদাহরণস্বরূপ, যদি আমি তৈরি করি

command="ls | grep -c '^'";

আউটপুটটি হ'ল:

Command: ls | grep -c '^'
ls: cannot access |: No such file or directory
ls: cannot access grep: No such file or directory
ls: cannot access '^': No such file or directory

পরবর্তী ব্যবহারের জন্য আমি কীভাবে এই জাতীয় কমান্ড (পাইপ / একাধিক কমান্ড সহ) ভেরিয়েবলে সঞ্চয় করতে পারি?


10
একটি ফাংশন ব্যবহার করুন!
gniourf_gniourf

উত্তর:


146

ব্যবহার করুন:

x="ls | wc"
eval "$x"
y=$(eval "$x")
echo "$y"

27
... (...) এর পরিবর্তে এখন প্রস্তাবিত backticks। y = $ (eval $ x) mywiki.wooledge.org/BashFAQ/082
জেমস

14
evalএকটি গ্রহণযোগ্য অভ্যাস শুধুমাত্র যদি আপনি আপনার ভেরিয়েবল 'বিষয়বস্তু আমাদের বিশ্বাস করেন। আপনি যদি চলমান থাকেন, বলুন x="ls $name | wc"(বা এমনকি x="ls '$name' | wc"), তাহলে এই কোডটি যদি ইনজেকশন বা সুবিধাগুলি বাড়ানোর দুর্বলতার জন্য একটি দ্রুত ট্র্যাক হয় যদি সেই পরিবর্তনশীলটি কম সুযোগ সুবিধাযুক্ত কেউ সেট করতে পারে। ( /tmpউদাহরণস্বরূপ, সমস্ত উপ-ডিরেক্টরিতে আইট্রেট করা ? আপনি সিস্টেমের প্রতিটি একক ব্যবহারকারীকে কল না করার জন্য আরও ভাল বিশ্বাস করবেন $'/tmp/evil-$(rm -rf $HOME)\'$(rm -rf $HOME)\'/')।
চার্লস ডাফি

9
evalএটি একটি বিশাল বাগ চুম্বক যা অপ্রত্যাশিত পার্সিং আচরণের ঝুঁকি সম্পর্কে সতর্কতা ছাড়াই কখনই সুপারিশ করা উচিত নয় (এমনকি @ চার্লসডফির উদাহরণ হিসাবে যেমন দূষিত স্ট্রিং ছাড়াই)। উদাহরণস্বরূপ, x='echo $(( 6 * 7 ))'এবং তারপর চেষ্টা করুন eval $x। আপনি "42" মুদ্রণের আশা করতে পারেন, তবে সম্ভবত তা তা করবে না। আপনি ব্যাখ্যা করতে পারেন কেন এটি কাজ করে না? আপনি কেন "সম্ভবত" বলেছিলেন তা ব্যাখ্যা করতে পারেন? যদি এই প্রশ্নের উত্তরগুলি আপনার কাছে স্পষ্ট না হয় তবে আপনার কখনই স্পর্শ করা উচিত নয় eval
গর্ডন ডেভিসন

1
@ স্টুডেন্ট, set -xকমান্ডগুলি চালিত হওয়ার লগ করতে আগে থেকে চালানোর চেষ্টা করুন , যা ঘটছে তা দেখতে এটি আরও সহজ করে তুলবে।
চার্লস ডাফি

1
@ স্টুডেন্ট আমি সাধারণ ভুলগুলি নির্দেশ করার জন্য শেলচেক ডটকমকেও সুপারিশ করব (এবং খারাপ অভ্যাসগুলি আপনি বেছে নেবেন না)।
গর্ডন ডেভিসন

41

ব্যবহার করবেন নাeval ! এটিতে স্বেচ্ছাসেবীর কোড প্রয়োগ কার্যকর করার একটি বড় ঝুঁকি রয়েছে।

বাশাএফএকিউ -50 - আমি একটি চলকটিতে একটি কমান্ড দেওয়ার চেষ্টা করছি, তবে জটিল ক্ষেত্রে সবসময় ব্যর্থ হয়।

ওয়ার্ড স্প্লিটিংয়ের কারণে শব্দটিকে বিভক্ত "${arr[@]}"করতে না দেওয়ার জন্য এটিকে একটি অ্যারে রাখুন এবং ডাবল-কোট দিয়ে সমস্ত শব্দ প্রসারিত করুন ।IFS

cmdArgs=()
cmdArgs=('date' '+%H:%M:%S')

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

declare -p cmdArgs
declare -a cmdArgs='([0]="date" [1]="+%H:%M:%S")'

এবং হিসাবে কমান্ড কার্যকর

"${cmdArgs[@]}"
23:15:18

(বা) bashকমান্ডটি চালাতে পুরোপুরি একটি ফাংশন ব্যবহার করুন ,

cmd() {
   date '+%H:%M:%S'
}

এবং ঠিক যেমন ফাংশন কল

cmd

পসিক্সের shকোনও অ্যারে নেই, তাই আপনি যে নিকটে আসতে পারেন তা হল অবস্থানগত পরামিতিগুলির উপাদানগুলির একটি তালিকা তৈরি করা। shমেল প্রোগ্রাম চালানোর জন্য এখানে একটি পসিক্স উপায়

# POSIX sh
# Usage: sendto subject address [address ...]
sendto() {
    subject=$1
    shift
    first=1
    for addr; do
        if [ "$first" = 1 ]; then set --; first=0; fi
        set -- "$@" --recipient="$addr"
    done
    if [ "$first" = 1 ]; then
        echo "usage: sendto subject address [address ...]"
        return 1
    fi
    MailTool --subject="$subject" "$@"
}

মনে রাখবেন যে এই পদ্ধতির কোনও পুনর্নির্দেশ ছাড়াই কেবল সাধারণ কমান্ডগুলি পরিচালনা করতে পারে। এটি লুপগুলির জন্য / বিবরণী ইত্যাদির জন্য পুনঃনির্দেশগুলি, পাইপলাইনগুলি পরিচালনা করতে পারে না

আর এক সাধারণ ব্যবহারের ক্ষেত্রে হ'ল curlএকাধিক শিরোলেখ ক্ষেত্র এবং পে-লোড নিয়ে চলমান । আপনি সর্বদা নীচের মতো আরগগুলি সংজ্ঞায়িত করতে পারেন এবং curlপ্রসারিত অ্যারে সামগ্রীতে প্রার্থনা করতে পারেন

curlArgs=('-H' "keyheader: value" '-H' "2ndkeyheader: 2ndvalue")
curl "${curlArgs[@]}"

আরেকটি উদাহরণ,

payload='{}'
hostURL='http://google.com'
authToken='someToken'
authHeader='Authorization:Bearer "'"$authToken"'"'

এখন যে ভেরিয়েবলগুলি সংজ্ঞায়িত করা হয়েছে, আপনার কমান্ড আরগগুলি সংরক্ষণ করার জন্য একটি অ্যারে ব্যবহার করুন

curlCMD=(-X POST "$hostURL" --data "$payload" -H "Content-Type:application/json" -H "$authHeader")

এবং এখন একটি সঠিক উদ্ধৃত প্রসারণ করুন

curl "${curlCMD[@]}"

এটি আমার পক্ষে কাজ করে না, আমি চেষ্টা করেছি Command=('echo aaa | grep a')এবং "${Command[@]}"আশা করি এটি আক্ষরিক অর্থে কমান্ডটি চালায় echo aaa | grep a। এটা হয় না। আমি ভাবছি যে প্রতিস্থাপনের কোনও নিরাপদ উপায় আছে কিনা eval, তবে মনে হয় যে প্রতিটি সমাধানের একই শক্তি রয়েছে evalএমনটি বিপজ্জনক হতে পারে। তাই না?
শিক্ষার্থী

সংক্ষেপে, মূল স্ট্রিংয়ে পাইপ '|' থাকলে এটি কীভাবে কাজ করবে?
শিক্ষার্থী

@ স্টুডেন্ট, যদি আপনার মূল স্ট্রিংটিতে একটি পাইপ থাকে তবে সেই স্ট্রিংটিতে কোড হিসাবে সম্পাদন করার জন্য বাশ পার্সারের অনিরাপদ অংশগুলি অতিক্রম করতে হবে। সেক্ষেত্রে স্ট্রিং ব্যবহার করবেন না; পরিবর্তে একটি ফাংশন ব্যবহার করুন: Command() { echo aaa | grep a; }- এর পরে আপনি কেবল চালাতে পারেন Command, বা result=$(Command), বা এর মতো।
চার্লস ডাফি

1
@ স্টুডেন্ট, ডান; তবে এটি ইচ্ছাকৃতভাবে ব্যর্থ হয় , কারণ আপনি যা করতে বলছেন তা সহজাতভাবে নিরাপত্তাহীন
চার্লস ডাফি


25
var=$(echo "asdf")
echo $var
# => asdf

এই পদ্ধতিটি ব্যবহার করে, কমান্ডটি তাত্ক্ষণিকভাবে মূল্যায়ন করা হয় এবং এর রিটার্ন মান সংরক্ষণ করা হয়।

stored_date=$(date)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015

ব্যাকটিক একই

stored_date=`date`
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015

ইওল এভাল ব্যবহার করা $(...)পরে তা মূল্যায়ন করে না

stored_date=$(eval "date")
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015

ইভাল ব্যবহার করে, এটি কখন evalব্যবহার করা হয় তা মূল্যায়ন করা হয়

stored_date="date" # < storing the command itself
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:05 EST 2015
# (wait a few seconds)
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:16 EST 2015
#                     ^^ Time changed

উপরের উদাহরণে, আপনার যদি আর্গুমেন্ট সহ একটি কমান্ড চালানোর দরকার হয় তবে সেগুলি আপনার সংরক্ষণের স্ট্রিংয়ে রাখুন

stored_date="date -u"
# ...

বাশ স্ক্রিপ্টগুলির জন্য এটি খুব কমই প্রাসঙ্গিক, তবে একটি শেষ নোট। সতর্কতা অবলম্বন করুন eval। আপনার নিয়ন্ত্রণ করা কেবলমাত্র স্ট্রিংগুলি, অবিশ্বস্ত ব্যবহারকারীর কাছ থেকে আসে না বা অবিশ্বস্ত ব্যবহারকারীর ইনপুট থেকে তৈরি স্ট্রিংগুলি কখনই আসে না।

  • কমান্ডটি উদ্ধৃত করার জন্য আমাকে স্মরণ করিয়ে দেওয়ার জন্য @ চার্লসডফিকে ধন্যবাদ জানাই!

কমান্ডটিতে পাইপ '|' রয়েছে এমন এটি আসল সমস্যাটি সমাধান করে না।
ছাত্র 21

@ নেটে, নোট করুন যে কেবলমাত্র এতে উপস্থিত থাকলে eval $stored_dateযথেষ্ট পরিমাণে ঠিক হতে পারে তবে এটি আরও নির্ভরযোগ্য। উদাহরণস্বরূপ ফাইনালের চারপাশে উদ্ধৃতিগুলি সহ এবং এর সাথে চালান । :)stored_datedateeval "$stored_date"str=$'printf \' * %s\\n\' *'; eval "$str""$str"
চার্লস ডাফি

@ চার্লস ডফি ধন্যবাদ, আমি উদ্ধৃতি দিতে ভুলে গেছি। আমি বাজি ধরব আমার লিটারটি চালানোর জন্য বিরক্ত করলে অভিযোগ হত have
নাট

0

বাশের জন্য, আপনার আদেশটি এইভাবে সংরক্ষণ করুন:

command="ls | grep -c '^'"

আপনার আদেশটি এভাবে চালান:

echo $command | bash

1
নিশ্চিত নয় তবে সম্ভবত কমান্ড চালানোর এই পদ্ধতিতে 'ইওল' ব্যবহারের যেমন ঝুঁকি রয়েছে।
ডেরেক হ্যাজেল

0

আমি বিভিন্ন বিভিন্ন পদ্ধতি চেষ্টা করেছি:

printexec() {
  printf -- "\033[1;37m$\033[0m"
  printf -- " %q" "$@"
  printf -- "\n"
  eval -- "$@"
  eval -- "$*"
  "$@"
  "$*"
}

আউটপুট:

$ printexec echo  -e "foo\n" bar
$ echo -e foo\\n bar
foon bar
foon bar
foo
 bar
bash: echo -e foo\n bar: command not found

আপনি দেখতে পাচ্ছেন, কেবল তৃতীয়টি "$@"সঠিক ফলাফল দিয়েছে।


0

অর্ডারটি নিবন্ধকরণে সাবধান থাকুন: X=$(Command)

এইটিকে ডাকার আগেও মৃত্যুদন্ড কার্যকর করা হয়। এটি পরীক্ষা এবং নিশ্চিত করতে, আপনি ক্যান্ডিং করুন:

echo test;
X=$(for ((c=0; c<=5; c++)); do
sleep 2;
done);
echo note the 5 seconds elapsed

-1
#!/bin/bash
#Note: this script works only when u use Bash. So, don't remove the first line.

TUNECOUNT=$(ifconfig |grep -c -o tune0) #Some command with "Grep".
echo $TUNECOUNT                         #This will return 0 
                                    #if you don't have tune0 interface.
                                    #Or count of installed tune0 interfaces.

-8

ভেরিয়েবলগুলিতে কমান্ড সংরক্ষণ করার প্রয়োজন নেই যদিও আপনার এটি পরে ব্যবহার করা দরকার। সাধারণ হিসাবে এটি চালানো। আপনি যদি ভেরিয়েবলটিতে সঞ্চয় করেন তবে আপনার eval"আপনার ভেরিয়েবলটি কার্যকর করতে" কোনও ধরণের বিবৃতি বা অযৌক্তিক শেল প্রক্রিয়া শুরু করতে হবে।


1
আমি যে কমান্ডটি সংরক্ষণ করব তা আমি প্রেরণকারী বিকল্পগুলির উপর নির্ভর করবে, সুতরাং আমার প্রোগ্রামের প্রচুর পরিমাণে প্রচুর শর্তযুক্ত বিবৃতি না দিয়ে পরে ব্যবহারের জন্য প্রয়োজনীয় কমান্ডটি সংরক্ষণ করা অনেক সহজ।
বেনিয়ামিন

1
@ বেঞ্জামিন, তারপরে কমপক্ষে অপশনটি ভেরিয়েবল হিসাবে সংরক্ষণ করুন, না কমান্ড। উদাহরণস্বরূপvar='*.txt'; find . -name "$var"
কুড়ুমী
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.