বাশ-এ একটি কমান্ডের আউটপুট পড়া


111

আমার স্ক্রিপ্টের একটি কমান্ডের আউটপুট একটি অ্যারেতে পড়তে হবে। কমান্ডটি উদাহরণস্বরূপ:

ps aux | grep | grep | x 

এবং এটি আউটপুট লাইনটিকে এভাবে লাইনে দেয়:

10
20
30

কমান্ড আউটপুট থেকে অ্যারেতে মানগুলি পড়তে হবে এবং তারপরে অ্যারের আকার তিনটির চেয়ে কম হলে আমি কিছু কাজ করব।


5
আরে @ বার্প, আপনার প্রশ্নের উত্তর দিন, যাতে আপনার ধরণটি পুরো সম্প্রদায়ের কোনও ড্রেন না হয়।
জেমস

9
@ জেমস ইস্যুটি এই বিষয়টির সাথে নয় যে তিনি তার প্রশ্নের উত্তর দিচ্ছেন না ... এটি একটি প্রশ্ন / এ সাইট। তিনি কেবল তাদের উত্তর হিসাবে চিহ্নিত করেন নি । তিনি তাদের চিহ্নিত করা উচিত। ইঙ্গিত। @
বারপ

4
দয়া করে @ বারপ, উত্তর হিসাবে প্রশ্ন চিহ্নিত করুন।
23:38

সম্পর্কিত: বাশ-এ কোনও ফাইলের সামগ্রীর মধ্য দিয়ে লুপিং যেহেতু প্রক্রিয়া প্রতিস্থাপনের মাধ্যমে কোনও কমান্ডের আউটপুট পড়া কোনও ফাইল থেকে পড়ার অনুরূপ।
কোডফোরস্টার

উত্তর:


161

অন্যান্য উত্তর (যা বরং ঘন হয়) ভঙ্গ হবে যদি কমান্ডের আউটপুট স্পেস রয়েছে বা মত অক্ষর উল্লিখিত glob *, ?, [...]

অ্যারেতে কমান্ডের আউটপুট পেতে প্রতিটি উপাদানকে এক রেখার সাহায্যে প্রয়োজনীয় 3 টি উপায় রয়েছে:

  1. বাশ≥ 4 mapfileএর সাথে এটি সবচেয়ে দক্ষ ব্যবহার করুন :

    mapfile -t my_array < <( my_command )
  2. অন্যথায়, আউটপুট পড়ার একটি লুপ (ধীর, তবে নিরাপদ):

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
  3. মন্তব্যগুলিতে চার্লস ডাফির পরামর্শ অনুসারে (ধন্যবাদ!), নিম্নলিখিত সংখ্যা 2 এ লুপ পদ্ধতির চেয়ে আরও ভাল অভিনয় করতে পারে:

    IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )

    দয়া করে নিশ্চিত হয়ে নিন যে আপনি ঠিক এই ফর্মটি ব্যবহার করেছেন, অর্থাত্ আপনার নিচেরটি রয়েছে তা নিশ্চিত করুন:

    • IFS=$'\n' readবিবৃতি হিসাবে একই লাইনে : এটি শুধুমাত্র বিবৃতি IFS জন্যread পরিবেশ পরিবর্তনশীল সেট করবে সুতরাং এটি আপনার স্ক্রিপ্টের মোটেও প্রভাব ফেলবে না। এই পরিবর্তনশীলটির উদ্দেশ্যটি readEOL চরিত্রটিতে স্ট্রিমটি ভাঙ্গতে বলা \n
    • -r: এটা গুরুত্বপূর্ণ. এটি ব্যাকস্ল্যাশগুলি এস্কেপ সিকোয়েন্স হিসাবে ব্যাখ্যা না করতে বলে read
    • -d '': দয়া করে -dবিকল্প এবং তার যুক্তির মধ্যে স্থানটি নোট করুন ''। আপনি যদি এখানে কোনও স্থান ছেড়ে না যান ''তবে কখনই দেখা যাবে না, কারণ এটি যখন বশ বিবৃতিটি বিশ্লেষণ করে তখন উদ্ধৃতি অপসারণের পদক্ষেপে অদৃশ্য হয়ে যাবে । এটি readনীল বাইটে পড়া বন্ধ করতে বলে । কিছু লোক এটি লিখেন -d $'\0'তবে এটি সত্যই প্রয়োজন হয় না। -d ''ভাল.
    • -a my_arrayস্ট্রিমটি পড়ার সময় readঅ্যারে পপুলেট করতে বলে my_array
    • আপনি ব্যবহার করা আবশ্যক printf '\0'বিবৃতি পর my_command , যাতে readআয় 0; এটি না করে আসলে এটি কোনও বড় বিষয় নয় (আপনি কেবলমাত্র একটি রিটার্ন কোড পাবেন 1, যা আপনি যদি ব্যবহার না করেন তবে ঠিক আছে set -e- যা আপনার কোনওভাবেই করা উচিত নয়), তবে কেবল এটি মনে রাখবেন। এটি ক্লিনার এবং আরও শব্দার্থগতভাবে সঠিক। মনে রাখবেন যে এটি থেকে আলাদা printf '', যা কোনও কিছুই আউটপুট দেয় না। খুশিভাবে সেখানে পড়া বন্ধ করার জন্য printf '\0'প্রয়োজনীয় একটি নাল বাইট মুদ্রণ করে read( -d ''বিকল্পটি মনে রাখবেন ?)।

যদি আপনি এটি করতে পারেন, অর্থাত্, যদি আপনি নিশ্চিত হন যে আপনার কোড বাশো 4 তে চলবে, তবে প্রথম পদ্ধতিটি ব্যবহার করুন। এবং আপনি এটিও খাটো দেখতে পারেন।

আপনি যদি ব্যবহার readকরতে চান তবে লাইনটি পড়ার সাথে সাথে কিছু প্রক্রিয়াজাতকরণ করতে চাইলে লুপের (পদ্ধতি 2) 3 এর চেয়ে বেশি সুবিধা থাকতে পারে: আপনার এতে সরাসরি অ্যাক্সেস রয়েছে ( $lineআমি যে উদাহরণ দিয়েছি তার পরিবর্তনের মাধ্যমে ), এবং আপনার ইতিমধ্যে পঠিত লাইনগুলিতে অ্যাক্সেস রয়েছে ( ${my_array[@]}আমি যে উদাহরণ দিয়েছি তা দিয়ে অ্যারের মাধ্যমে )।

নোট যা mapfileপ্রতিটি লাইনে পড়ার জন্য কলব্যাককে বিস্তৃত করার একটি উপায় সরবরাহ করে এবং বাস্তবে আপনি প্রতিটি এন লাইনে পড়া কেবল এই কলব্যাকটি কল করতে বলতে পারেন; একটি help mapfileবিকল্প -Cএবং এর বিকল্পগুলি দেখুন -c। (এ সম্পর্কে আমার মতামতটি হ'ল এটি কিছুটা জটিল) তবে কখনও কখনও ব্যবহার করা যেতে পারে যদি আপনার কাছে কেবল সহজ জিনিস থাকে - তবে কেন এটি এমনকি প্রথম স্থানে কার্যকর করা হয়েছিল তা আমি সত্যি বুঝতে পারি না!)।


এখন আমি আপনাকে কেন নীচের পদ্ধতিটি বলব:

my_array=( $( my_command) )

স্পেস থাকলে ভাঙা হয়:

$ # I'm using this command to test:
$ echo "one two"; echo "three four"
one two
three four
$ # Now I'm going to use the broken method:
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # As you can see, the fields are not the lines
$
$ # Now look at the correct method:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!

তারপরে কিছু লোক IFS=$'\n'এটি ঠিক করার জন্য ব্যবহারের পরামর্শ দিবে :

$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!

তবে এখন গ্লোব সহ আরও একটি কমান্ড ব্যবহার করা যাক :

$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?

যে কারণ আমি নামক কোনো ফাইল আছে tবর্তমান ডিরেক্টরির মধ্যে ... এবং এই ফাইলের নাম দ্বারা মেলানো হয় উল্লিখিত glob [three four] ... এই সময়ে কিছু লোক ব্যবহার সুপারিশ করবে set -fনিষ্ক্রিয় globbing না; কিন্তু এটি তাকান: আপনার পরিবর্তন করতে হবে IFSএবং ব্যবহার set -fএকটি ফিক্স পাবে ভাঙা কৌশল (এবং আপনি এমনকি এটি ঠিক করে নিচ্ছেন না)! এটি করার সময় আমরা শেলের সাথে লড়াই করছি না , শেলের সাথে কাজ করছি না ।

$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'

এখানে আমরা শেল নিয়ে কাজ করছি!


4
এটি দুর্দান্ত, আমি mapfileএর আগে কখনও শুনিনি, আমি ঠিক বছরের পর বছর ধরে মিস করছি। আমার ধারণা, এর সাম্প্রতিক সংস্করণগুলিতে bashঅনেকগুলি নতুন নতুন বৈশিষ্ট্য রয়েছে, আমার ডকসটি পড়তে এবং একটি দুর্দান্ত চিটপত্র লিখতে আমার কিছুদিন ব্যয় করা উচিত।
জিন পাভলোভস্কি

6
বিটিডব্লিউ, < <(command)শেল স্ক্রিপ্টগুলিতে এই সিনট্যাক্সটি ব্যবহার করতে শেবাং লাইনটি হওয়া উচিত #!/bin/bash- যদি চালানো হয় তবে বাক্স #!/bin/shএকটি সিনট্যাক্স ত্রুটির সাথে প্রস্থান করবে।
জিন পাভলোভস্কি

1
@ জেনিপ্যাভলভস্কির সহায়ক নোটটি প্রসারিত করে, স্ক্রিপ্টটি অবশ্যই বাশ কমান্ডের সাথে চালাতে হবে bash my_script.sh, sh কমান্ড নয়sh my_script.sh
ভিটো

2
@ ভিটো: প্রকৃতপক্ষে, এই উত্তরটি কেবল বাশের জন্য, তবে এটি কোনও সমস্যা হওয়া উচিত নয়, কারণ কঠোরভাবে মেনে চলার পসিক্স শেলগুলি অ্যারেও বাস্তবায়ন করে না ( shএবং dashঅ্যারে সম্পর্কে কিছুই জানে না, অবশ্যই, ব্যতীত, অবস্থানগত পরামিতি $@অ্যারে)।
gniourf_gniourf

3
অন্য বিকল্প যে ব্যাশ 4.0 প্রয়োজন না, বিবেচনা করার সময় এটা IFS=$'\n' read -r -d '' -a my_array < <(my_command && printf '\0')এটি সঠিকভাবে ব্যাশ 3.x উভয় কাজ, এবং থেকে একটি ব্যর্থ প্রস্থান অবস্থা মাধ্যমে প্রেরণ করা - my_commandথেকে read
চার্লস ডাফি

86

তুমি ব্যবহার করতে পার

my_array=( $(<command>) )

কমান্ডের আউটপুট <command>অ্যারেতে সঞ্চয় করতে my_array

আপনি ব্যবহার করে যে অ্যারের দৈর্ঘ্য অ্যাক্সেস করতে পারেন

my_array_length=${#my_array[@]}

এখন দৈর্ঘ্য সংরক্ষণ করা হয় my_array_length


19
যদি $ (কমান্ড) এর আউটপুটে ফাঁকা স্থান এবং একাধিক লাইন থাকে? আমি "$ (কমান্ড)" যুক্ত করেছি এবং এটি সমস্ত লাইন থেকে সমস্ত আউটপুট অ্যারের প্রথম [0] উপাদানে রাখে।
ikwyl6

3
@ ikwyl6 একটি workaround একটি ভেরিয়েবল কমান্ড আউটপুট বরাদ্দ করা হয় এবং তারপরে একটি অ্যারে তৈরি বা একটি অ্যারে এ যোগ করা হয়। VAR="$(<command>)"এবং তারপরে my_array=("$VAR")বাmy_array+=("$VAR")
ভিটো

10

কল্পনা করুন যে আপনি ফাইলগুলি এবং ডিরেক্টরিগুলির নাম (বর্তমান ফোল্ডারের নীচে) একটি অ্যারেতে রেখেছেন এবং এর আইটেমগুলি গণনা করতে চলেছেন। লিপিটি এমন হবে;

my_array=( `ls` )
my_array_length=${#my_array[@]}
echo $my_array_length

অথবা, আপনি নিম্নলিখিত স্ক্রিপ্টটি যুক্ত করে এই অ্যারেটির উপরে পুনরাবৃত্তি করতে পারেন:

for element in "${my_array[@]}"
do
   echo "${element}"
done

দয়া করে নোট করুন যে এটি মূল ধারণা এবং ইনপুটটিকে আগে স্যানিটাইজড হিসাবে বিবেচনা করা হয়, অর্থাত্ অতিরিক্ত অক্ষর মুছে ফেলা, খালি স্ট্রিংগুলি পরিচালনা করা ইত্যাদি which (যা এই থ্রেডের বিষয় থেকে বাইরে)।


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