আমি কীভাবে "ফাইন্ড" কমান্ডের ফলাফলগুলিকে বাশে অ্যারে হিসাবে সংরক্ষণ করতে পারি


92

findঅ্যারে হিসাবে থেকে ফলাফলটি সংরক্ষণ করার চেষ্টা করছি । আমার কোডটি এখানে:

#!/bin/bash

echo "input : "
read input

echo "searching file with this pattern '${input}' under present directory"
array=`find . -name ${input}`

len=${#array[*]}
echo "found : ${len}"

i=0

while [ $i -lt $len ]
do
echo ${array[$i]}
let i++
done

আমি বর্তমান ডিরেক্টরিতে 2 .txt ফাইল পাই। সুতরাং ফলস্বরূপ আমি '2' আশা করি ${len}। তবে এটি মুদ্রণ করে 1. কারণটি হ'ল এটি findএকটি উপাদান হিসাবে সমস্ত ফলাফল গ্রহণ করে। আমি এটা কিভাবে ঠিক করবো?

পিএস
আমি একই ধরণের সমস্যার বিষয়ে স্ট্যাকওভারফ্লোতে বেশ কয়েকটি সমাধান পেয়েছি। তবে এগুলি কিছুটা আলাদা তাই আমি আমার ক্ষেত্রে আবেদন করতে পারি না। লুপের আগে আমাকে ফলাফলগুলি পরিবর্তনশীলে সংরক্ষণ করতে হবে। আবার ধন্যবাদ.

উত্তর:


133

লিনাক্স ব্যবহারকারীদের জন্য 2020 আপডেট করুন:

আপনার যদি বাশ (৪.৪-আলফা বা আরও ভাল) সংস্করণ থাকে তবে আপনি সম্ভবত লিনাক্সে থাকলে এটি করা উচিত, তবে আপনার উচিত বেনজামিন ডাব্লু এর উত্তরটি ব্যবহার করা ।

আপনি যদি ম্যাক ওএসে থাকেন তবে আমি কোনটি এখনও পরীক্ষা করে দেখেছি - এখনও ব্যাশ ৩.২ ব্যবহার করেছেন বা অন্যথায় কোনও পুরানো ব্যাশ ব্যবহার করছেন তবে পরবর্তী বিভাগে চালিয়ে যান।

4.3 বা তার আগে বাশার উত্তর

এখানে আউটপুট পাওয়ার জন্য এক সমাধান findএকটি মধ্যে bashঅ্যারে:

array=()
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done < <(find . -name "${input}" -print0)

এটি মুশকিল কারণ সাধারণভাবে ফাইলের নামগুলিতে স্পেস, নতুন লাইন এবং অন্যান্য স্ক্রিপ্ট-প্রতিকূল অক্ষর থাকতে পারে। findফাইলের নামগুলি একে অপরের থেকে নিরাপদে পৃথকভাবে ব্যবহার এবং ব্যবহারের একমাত্র উপায় হ'ল -print0ফাইল নামগুলি নাল অক্ষর দ্বারা পৃথক করা মুদ্রণ করা। যদি ব্যাশের readarray/ mapfileফাংশনগুলি নাল-বিচ্ছিন্ন স্ট্রিংগুলিকে সমর্থন করে তবে এটি কোনও অসুবিধে হবে না। বাশের readকাজটি করে এবং এটি আমাদের উপরের লুপের দিকে নিয়ে যায়।

[এই উত্তরটি মূলত ২০১৪ সালে লেখা হয়েছিল you আপনার কাছে বাশের সাম্প্রতিক সংস্করণ থাকলে নীচের আপডেটটি দেখুন]]

কিভাবে এটা কাজ করে

  1. প্রথম লাইনটি একটি খালি অ্যারে তৈরি করে: array=()

  2. প্রতিবার readস্টেটমেন্টটি কার্যকর করা হলে, স্ট্যান্ডার্ড ইনপুট থেকে একটি নাল-বিচ্ছিন্ন ফাইলের নামটি পড়া হয়। -rবিকল্প বলে readব্যাকস্ল্যাশ অক্ষর একা ছেড়ে। -d $'\0'বলে readযে ইনপুট নাল বিভাজিত হতে হবে। যেহেতু আমরা নাম বাদ read, শেল ডিফল্ট নামে ইনপুট রাখে: REPLY

  3. array+=("$REPLY")বিবৃতি অ্যারের নতুন ফাইলের নাম appends array

  4. চূড়ান্ত লাইনটি পুনর্নির্দেশ এবং কমান্ড প্রতিস্থাপনের সাথে একত্রিত findকরে whileলুপের স্ট্যান্ডার্ড ইনপুটটির আউটপুট সরবরাহ করে ।

প্রক্রিয়া বিকল্প ব্যবহার কেন?

আমরা যদি প্রক্রিয়া বিকল্প ব্যবহার না করি তবে লুপটি এইভাবে লেখা যেতে পারে:

array=()
find . -name "${input}" -print0 >tmpfile
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done <tmpfile
rm -f tmpfile

উপরের আউটপুটটি findএকটি অস্থায়ী ফাইলে সংরক্ষণ করা হয় এবং সেই ফাইলটি লুপের স্ট্যান্ডার্ড ইনপুট হিসাবে ব্যবহৃত হয়। প্রক্রিয়া প্রতিস্থাপনের ধারণাটি এই জাতীয় অস্থায়ী ফাইলগুলিকে অপ্রয়োজনীয় করে তোলা। সুতরাং, এর পরিবর্তে whileলুপটি এর স্টিডিনটি পেতে tmpfile, আমরা এটির স্টিডিনটি পেতে পারি <(find . -name ${input} -print0)

প্রক্রিয়া প্রতিস্থাপন ব্যাপকভাবে দরকারী। কমান্ড একটি ফাইল থেকে পড়তে চায় এমন অনেক জায়গায় , আপনি <(...)কোনও ফাইলের নামের পরিবর্তে প্রক্রিয়া বিকল্পটি নির্দিষ্ট করতে পারেন । একটি অনুরূপ ফর্ম রয়েছে, >(...)এটি একটি ফাইলের নামের জায়গায় ব্যবহার করা যেতে পারে যেখানে কমান্ড ফাইলটি লিখতে চায় ।

অ্যারেগুলির মতো, প্রক্রিয়া প্রতিস্থাপন বাশ এবং অন্যান্য উন্নত শেলগুলির বৈশিষ্ট্য। এটি পসিক্স স্ট্যান্ডার্ডের অংশ নয়।

বিকল্প: সর্বশেষ পাইপ

যদি ইচ্ছা lastpipeহয় তবে প্রক্রিয়া বিকল্পের পরিবর্তে ব্যবহার করা যেতে পারে (টুপি টিপ: সিজার ):

set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS=  read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array

shopt -s lastpipeবর্তমান শেলের পাইপলাইনে (ব্যাকগ্রাউন্ড নয়) সর্বশেষ কমান্ড চালাতে বাশকে বলে। এইভাবে, arrayপাইপলাইনটি সম্পূর্ণ হওয়ার পরে অবশেষে অস্তিত্ব রয়েছে। কারণ lastpipeকেবল তখনই কার্যকর হয় যদি কাজের নিয়ন্ত্রণ বন্ধ থাকে, আমরা চালাই set +m। (কোনও স্ক্রিপ্টে, কমান্ড লাইনের বিপরীতে, চাকরি নিয়ন্ত্রণ ডিফল্টভাবে বন্ধ রয়েছে))

অতিরিক্ত নোট

নিম্নলিখিত কমান্ড শেল অ্যারে নয় শেল পরিবর্তনশীল তৈরি করে:

array=`find . -name "${input}"`

যদি আপনি কোনও অ্যারে তৈরি করতে চান তবে আপনাকে খুঁজে পাওয়ার আউটপুটটি জুড়ে প্যারেন্স লাগাতে হবে। সুতরাং, নির্বোধভাবে, একটি পারে:

array=(`find . -name "${input}"`)  # don't do this

সমস্যাটি হ'ল শেল এর ফলাফলগুলিতে শব্দের বিভাজন সম্পাদন করে findযাতে অ্যারের উপাদানগুলিকে আপনি চান তা গ্যারান্টিযুক্ত না করে।

2019 আপডেট করুন

সংস্করণ ৪.৪-আলফা দিয়ে শুরু করে, বাশ এখন একটি -dবিকল্প সমর্থন করে যাতে উপরের লুপটি আর প্রয়োজন হয় না। পরিবর্তে, একটি ব্যবহার করতে পারেন:

mapfile -d $'\0' array < <(find . -name "${input}" -print0)

এ সম্পর্কিত আরও তথ্যের জন্য, দয়া করে বেনজামিন ডাব্লু এর উত্তর দেখুন (এবং উপস্থাপন করুন) ।


4
@ জুনিয়ৌং এটি খুশী হয়েছে আমি প্রক্রিয়া প্রতিস্থাপনের একটি অংশ যুক্ত করেছি।
1024

4
@ রোকলাইট এটি একটি ভাল পর্যবেক্ষণ তবে অসম্পূর্ণ। যদিও এটি সত্য যে আমরা একাধিক শব্দের মধ্যে বিভক্ত হই না, তবুও IFS=আমাদের ইনপুট লাইনের শুরু বা শেষ থেকে সাদা স্থান অপসারণ এড়াতে হবে। আউটপুট এর read var <<<' abc '; echo ">$var<"সাথে আউটপুট তুলনা করে আপনি এটি সহজে পরীক্ষা করতে পারেন IFS= read var <<<' abc '; echo ">$var<"। পূর্ববর্তী ক্ষেত্রে, আগের এবং পরে থাকা স্থানগুলি abcসরিয়ে ফেলা হয়। পরবর্তীকালে, তারা না। হোয়াইটস্পেসের সাথে শুরু হওয়া বা শেষ হওয়া ফাইলের নামগুলি অস্বাভাবিক হতে পারে তবে এটি উপস্থিত রয়েছে, আমরা সেগুলি সঠিকভাবে প্রক্রিয়া করতে চাই।
1024

4
হাই, আমি আপনার কোডটি কার্যকর করার পরে আমি অপ্রত্যাশিত টোকেনের কাছে বার্তা সিনট্যাক্স ত্রুটি <' পেয়েছি <<(aaa / -not -nevermt "$ গত_বিল্ড_টাইমস্ট্যাম্প_ভি" -প্রকার এফ-প্রিন্ট0) '
সিয়েনকিউইজ

4
একটি দ্রষ্টব্য: এর ''পরিবর্তে $'\0'n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
সরলটি

4
@ থেইগল আমি ধরে নিলাম আপনি লেখার ইচ্ছা করেছিলেন BLAH=$(find . -name '*.php')। উত্তরে আলোচিত হিসাবে, এই পদ্ধতিটি সীমিত ক্ষেত্রে কাজ করবে তবে এটি সমস্ত ফাইলের সাথে সাধারণভাবে কাজ করবে না এবং ওপি যেমন প্রত্যাশা করেছিল তেমন একটি অ্যারে তৈরি করে না ।
1024

35

4.4 বাশ / এ একটি -dবিকল্প চালু করেছে , সুতরাং এটি এখন দিয়ে সমাধান করা যেতে পারেreadarraymapfile

readarray -d '' array < <(find . -name "$input" -print0)

এমন পদ্ধতির জন্য যা ফাঁকা, নতুনলাইনগুলি এবং গ্লোব্বিং অক্ষরগুলি সহ নির্বিচারে ফাইলের সাথে কাজ করে। এটির জন্য আপনার findসমর্থনগুলি প্রয়োজন -print0যেমন উদাহরণস্বরূপ জিএনইউ সন্ধান করে।

ম্যানুয়াল থেকে (অন্যান্য বিকল্প বাদ দিয়ে):

mapfile [-d delim] [array]

-d
প্রথম অক্ষরটি delimনতুন লাইন না করে প্রতিটি ইনপুট লাইনটি শেষ করতে ব্যবহৃত হয়। যদি delimখালি স্ট্রিং হয় তবে mapfileকোনও লাইন যখন এটি একটি NUL অক্ষর পড়ে তখন এটি শেষ করবে।

এবং readarrayএটি একটি প্রতিশব্দ মাত্র mapfile


18

আপনি যদি bash4 বা তার বেশি ব্যবহার করে থাকেন তবে আপনি নিজের ব্যবহারটি এর findসাথে প্রতিস্থাপন করতে পারেন

shopt -s globstar nullglob
array=( **/*"$input"* )

**প্যাটার্ন সক্রিয় globstarম্যাচ 0 বা একাধিক ডিরেক্টরি, প্যাটার্ন বর্তমান ডিরেক্টরির মধ্যে একটি অবাধ গভীরতা মেলে দেয়। nullglobবিকল্প ব্যতীত , প্যাটার্নটি (প্যারামিটার সম্প্রসারণের পরে) আক্ষরিকভাবে চিকিত্সা করা হয়, সুতরাং কোনও মিলের সাথে আপনার খালি অ্যারের পরিবর্তে একক স্ট্রিংয়ের সাথে অ্যারে থাকবে না।

dotglobআপনি যদি লুকানো ডিরেক্টরিগুলি (যেমন .ssh) দেখতে চান এবং লুকানো ফাইলগুলি (যেমন .bashrc) মেলাতে চান তবে প্রথম লাইনে বিকল্পটি যুক্ত করুন ।


4
সম্ভবত nullglobখুব…
কোজিরো

4
হ্যাঁ, আমি সবসময় এটি ভুলে যাই।
চিপনার

4
নোট করুন যে dotglobএটিতে লুকানো ফাইল এবং ডিরেক্টরি অন্তর্ভুক্ত থাকবে না, যদি না সেট করা থাকে (এটি হয়ত চাওয়া বা নাও চাইলেও এটি উল্লেখ করার মতো)।
gniourf_gniourf

10

আপনি কিছু চেষ্টা করতে পারেন

array=(`find . -type f | sort -r | head -2`)
, এবং অ্যারের মানগুলি মুদ্রণের জন্য, আপনি প্রতিধ্বনির মতো কিছু চেষ্টা করতে পারেন "${array[*]}"


8
ফাঁকা স্থান বা গ্লোব অক্ষর সহ ফাইলের নাম থাকলে বিরতি।
gniourf_gniourf

1

নিম্নলিখিতটি ম্যাকোজে বাশ এবং জেড শেল উভয়ের জন্যই কাজ করছে বলে মনে হচ্ছে।

#! /bin/sh

IFS=$'\n'
paths=($(find . -name "foo"))
unset IFS

printf "%s\n" "${paths[@]}"

এটি ফাঁকা স্থান এবং অন্যান্য বিশেষ অক্ষরযুক্ত ফাইলগুলির সাথে কাজ করে, তাদের নামে লাইনব্রেক করার ফাইলগুলির (স্বীকৃত বিরল) ক্ষেত্রে ব্যর্থ হয়। আপনার সাথে একটি পরীক্ষার জন্য তৈরি করতে পারেনprintf "%b" "file name with spaces, a star * ...\012and a second line\0" | xargs -0 touch
Stéphane Gourichon

-1

ব্যাশে, $(<any_shell_cmd>)একটি কমান্ড চালাতে এবং আউটপুট ক্যাপচার করতে সহায়তা করে। এই পাসিং IFSসঙ্গে \nবিভেদক একটি অ্যারের যে রূপান্তর করতে সাহায্য করে থাকে।

IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")

4
এটি findঅ্যারের মধ্যে ফলাফলের প্রথম ফাইল পাবেন ।
বেনিয়ামিন ডাব্লু।

-2

আপনি এই মত করতে পারে:

#!/bin/bash
echo "input : "
read input

echo "searching file with this pattern '${input}' under present directory"
array=(`find . -name '*'${input}'*'`)

for i in "${array[@]}"
do :
    echo $i
done

4
ধন্যবাদ অনেক. তবে @ নানসান ইশারা করে হিসাবে, ফাইলনামে খালি স্থানগুলি আমার প্রোগ্রামে বিবেচনা করা উচিত। যাইহোক ধন্যবাদ!
জুনিয়ং ওহ

-3

আমার জন্য, এটি সাইগউইন সম্পর্কে ভাল কাজ করেছে:

declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")")
for nm in "${names[@]}"
do
    echo "$nm"
done

এটি স্পেসগুলির সাথে কাজ করে তবে ডিরেক্টরিগুলির নামগুলিতে ডাবল কোট (") দিয়ে নয় (যা উইন্ডোজ পরিবেশে অনুমোদিত নয়)।

-প্রিন্টফ বিকল্পে স্থানটি সাবধান করুন।


4
ভাঙা এবং বিপজ্জনক : উদ্ধৃতিগুলি পরিচালনা করবে না এবং এটি নির্বিচারে কোড ইনজেকশন সাপেক্ষে। ব্যবহার করবেন না.
gniourf_gniourf

4
দেখে মনে হচ্ছে কেউ মুছে ফেলার জন্য এই পোস্টটিকে পতাকাঙ্কিত করেছে। "এটি ভুল হয়" তাই মুছে ফেলার কোনও কারণ নয়। ব্যবহারকারী উত্তর দেওয়ার চেষ্টা করেছিলেন, এটি বিষয়টিতে রয়েছে এবং উত্তরের মানদণ্ড পূরণ করে। ডাউনভোট বোতামটি মুছে ফেলা বোতামটি নয়, দরকারীতা এবং সঠিকতা পরিমাপ করতে ব্যবহৃত হয়।
ফ্রেমবট

4
গনিউরফ যেমন উল্লেখ করেছেন, এটি এমন পরিবেশের জন্য নয় যেখানে অন্যরা আপনার সিস্টেমে বিকল্পগুলি প্রবেশ করে, যেমন ওয়েব পৃষ্ঠাগুলি। তবে সবাই সেই পরিবেশের জন্য প্রোগ্রাম করে না। আমি এটি ডিরেক্টরিতে ফাইলগুলির নামকরণের জন্য ব্যবহার করেছি।
আর ঝুঁকি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.