লিনাক্স-এ শেল ফাংশন সন্ধান করুন?


185

findশেলের মধ্যে আমি যে ফাংশনটি সংজ্ঞায়িত করেছি তা কার্যকর করার কোনও উপায় আছে ? উদাহরণ স্বরূপ:

dosomething () {
  echo "doing something with $1"
}
find . -exec dosomething {} \;

তার ফলাফল:

find: dosomething: No such file or directory

একটি উপায় আছে পেতে হয় finds 'এর -execদেখতে dosomething?

উত্তর:


262

যেহেতু কেবল শেলই শেল ফাংশন চালাতে জানে তাই আপনাকে একটি ফাংশন চালাতে শেল চালাতে হবে। আপনার রফতানির জন্য আপনার ফাংশনটি চিহ্নিত করতে export -fহবে, অন্যথায় সাবশেলগুলি তাদের উত্তরাধিকারসূত্রে আসবে না:

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;

7
আপনি আমাকে বীট. উপায় দ্বারা আপনি ব্যবহারের পরিবর্তে কোটাগুলির মধ্যে ব্রেসগুলি রাখতে পারেন $0
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

20
যদি আপনি find . -exec bash -c 'dosomething "$0"' {} \;এটি ফাইলের
নামগুলিতে

3
@ অ্যালেক্সেন্ডার: যা ডাবল-কোট, ব্যাককোট, ডলার-চিহ্ন, কিছু পালানোর কম্বো ইত্যাদির সাথে ফাইলের নামগুলি ব্যর্থ হবে ...
গর্ডন ডেভিসন

4
এছাড়াও খেয়াল করুন যে আপনার ফাংশনটি কল করছে এমন কোনও ফাংশন আপনি সেগুলি রফতানি না করে উপলব্ধ হবে না।
হারাবান

3
এটি কেবল export -fব্যাশের কয়েকটি সংস্করণে কাজ করবে। এটি প্যাসিক্স নয়, ক্রসপ্ল্যাটফোর্ড নয়, /bin/shএটির সাথে একটি ত্রুটি থাকবে
Коптев r

120
find . | while read file; do dosomething "$file"; done

10
সুন্দর সমাধান। ফাংশনটি রফতানি করার বা পাল্টে থাকা আর্গুমেন্টের জগাখিচির প্রয়োজন হয় না এবং সম্ভবত এটি আরও কার্যকরী যেহেতু এটি প্রতিটি ফাংশন সম্পাদন করার জন্য সাবসেলগুলি স্প্যান করছে না।
টম

19
তবে মনে রাখবেন যে এটি নতুন লাইনে থাকা ফাইলের নামগুলি ভেঙে দেবে।
চিপনার

3
এটি আরও "শেল'শ" কারণ আপনার গ্লোবাল ভেরিয়েবল এবং ফাংশনগুলি উপলব্ধ হবে, প্রতিবার সম্পূর্ণ নতুন শেল / পরিবেশ তৈরি না করে। অ্যাডামের পদ্ধতিটি চেষ্টা করার পরে এবং সমস্ত প্রকারের পরিবেশগত সমস্যাগুলির মধ্যে চলে যাওয়ার পরে এটি শক্তভাবে শিখেছে। এই পদ্ধতিটি সমস্ত রফতানির সাথে আপনার বর্তমান ব্যবহারকারীর শেলকেও দূষিত করে না এবং কম ডিসসিপাইন প্রয়োজন requires
রায় ফস 18

কোনও সাব শেল ছাড়াই স্বীকৃত উত্তরের চেয়ে বেশি দ্রুত! চমৎকার! ধন্যবাদ!
বিল হেলার

2
এছাড়াও আমি while readলুপের জন্য পরিবর্তন করে আমার সমস্যাটি স্থির করেছি ; for item in $(find . ); do some_function "${item}"; done
ব্যবহারকারী5359531

22

জ্যাকের উপরের উত্তরটি দুর্দান্ত তবে কয়েকটি সমস্যা রয়েছে যা সহজেই কাটিয়ে উঠতে পারে:

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

এটি লাইনফিডের পরিবর্তে ডিলিমিটার হিসাবে নাল ব্যবহার করে, সুতরাং লাইনফিডগুলির সাথে ফাইলের নামগুলি কাজ করবে। এটি -rপতাকা ব্যবহার করে যা ব্যাকস্ল্যাশ পলায়ন নিষ্ক্রিয় করে, ফাইল নামগুলিতে ব্যাকস্ল্যাশগুলি কাজ করবে না। এটি IFSযাতে পরিষ্কার হয় যাতে নামগুলিতে সম্ভাব্য পেছনের শ্বেত স্থানগুলি বাতিল না হয়।


1
এটি ভাল /bin/bashতবে এতে কাজ করবে না /bin/sh। কি সুন্দর.
Роман Коптев

@ РоманКоптев কত ভাগ্যবান যে কমপক্ষে এটি / বিন / ব্যাশে কাজ করে।
এসডেনহ্যাম

21

{}নীচে প্রদর্শিত হিসাবে উদ্ধৃতি যুক্ত করুন :

export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;

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


1
এটি ব্যবহারের সঠিক উপায় নয় {}। এটি ডাবল উদ্ধৃতি সম্বলিত একটি ফাইলের জন্য ভেঙে যাবে। touch '"; rm -rf .; echo "I deleted all you files, haha। উফ।
gniourf_gniourf

হ্যাঁ, এটি খুব খারাপ। এটি ইনজেকশন দ্বারা শোষণ করা যেতে পারে। খুব অনিরাপদ। এটি ব্যবহার করবেন না!
ডোমিনিক

1
@kdubs: কমান্ড-স্ট্রিংয়ের মধ্যে $ 0 (অব্যক্ত) ব্যবহার করুন এবং ফাইলের নামটিকে প্রথম আর্গুমেন্ট হিসাবে পাস করুন: -exec bash -c 'echo $0' '{}' \;নোট করুন যে ব্যবহার করার সময় bash -c, when 0 প্রথম আর্গুমেন্ট, স্ক্রিপ্টের নাম নয়।
এসডেনহ্যাম

10

প্রক্রিয়াজাতকরণ ফলাফল বাল্ক

দক্ষতা বৃদ্ধির জন্য, অনেকে xargsবাল্কের ফলাফলগুলি প্রক্রিয়া করতে ব্যবহার করেন তবে এটি খুব বিপজ্জনক। এর ফলে একটি বিকল্প পদ্ধতি চালু হয়েছিল findযা প্রচুর পরিমাণে ফলাফল কার্যকর করে।

নোট যে যদিও এই পদ্ধতি POSIX- একটি প্রয়োজন উদাহরণস্বরূপ মত কিছু আদেশ সহকারে লাগতে পারে findআছে {}কমান্ডের শেষে।

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

findbashসেই সমস্ত forএকটিতে ফাংশন সম্পাদন করে those যুক্তিগুলির মাধ্যমে একক কল এবং -লুপ পুনরাবৃত্তির কাছে আর্গুমেন্ট হিসাবে অনেকগুলি ফলাফল পাস করবে dosomething

উপরের সমাধানটি এখানে আর্গুমেন্ট শুরু করে $1, যার কারণে একটি রয়েছে _(যা উপস্থাপন করে $0)।

একের পর এক প্রক্রিয়াজাতকরণের ফলাফল

একইভাবে, আমি মনে করি যে গৃহীত শীর্ষস্থানীয় উত্তরগুলি সঠিক হওয়া উচিত

export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;

এটি কেবল আরও বুদ্ধিমান নয়, কারণ আর্গুমেন্টগুলি সর্বদা শুরু হওয়া উচিত $1, তবে $0ফাইল নামটি findশেলের যদি বিশেষ অর্থ থাকে তবে এটি ব্যবহার করা অপ্রত্যাশিত আচরণের কারণ হতে পারে ।


9

স্ক্রিপ্টটি নিজেই কল করুন, প্রতিটি আইটেমকে আর্গুমেন্ট হিসাবে পাওয়া যায়:

#!/bin/bash

if [ ! $1 == "" ] ; then
   echo "doing something with $1"
   exit 0
fi

find . -exec $0 {} \;

exit 0

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


শীতল ধারণা তবে খারাপ শৈলী: দুটি উদ্দেশ্যে একই স্ক্রিপ্ট ব্যবহার করে। যদি আপনি আপনার বিনের ফাইলগুলির সংখ্যা হ্রাস করতে চান / তবে আপনি আপনার সমস্ত স্ক্রিপ্টগুলি একক একত্রে মার্জ করতে পারেন যার শুরুতে বড় কেস ক্লজ রয়েছে। খুব পরিষ্কার সমাধান, তাই না?
ব্যবহারকারী 829755

এই find: ‘myscript.sh’: No such file or directoryহিসাবে শুরু করা হলে এটি ব্যর্থ হবে তা উল্লেখ করার জন্য নয় bash myscript.sh...
ক্যামুসেনসি

5

আপনারা যারা বাশ ফাংশন সন্ধান করছেন যা বর্তমান ডিরেক্টরিতে সমস্ত ফাইলগুলিতে প্রদত্ত কমান্ড কার্যকর করবে, আমি উপরের উত্তরগুলি থেকে একটি সংকলন করেছি:

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

মনে রাখবেন যে এটি ফাঁকা স্থান সহ ফাইলের নামগুলির সাথে ব্রেক হয় (নীচে দেখুন)

উদাহরণ হিসাবে, এই ফাংশনটি গ্রহণ করুন:

world(){
    sed -i 's_hello_world_g' "$1"
}

বলুন যে আমি বর্তমান ডিরেক্টরিতে সমস্ত ফাইলগুলিতে হ্যালো সমস্ত দুনিয়াতে বিশ্বকে পরিবর্তন করতে চেয়েছিলাম। আমি করবো:

toall world

ফাইলের নামগুলিতে কোনও চিহ্ন সহ নিরাপদ থাকতে, ব্যবহার করুন:

toall(){
    find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}

(তবে আপনার একটি দরকার find হ্যান্ডলগুলি -print0যেমন, জিএনইউ দরকার find)।


3

আমি অনুসরণ হিসাবে সবচেয়ে সহজ উপায় খুঁজে পাই, একক করণে দুটি কমান্ড পুনরাবৃত্তি

func_one () {
  echo "first thing with $1"
}

func_two () {
  echo "second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done

3

রেফারেন্সের জন্য, আমি এই দৃশ্যটি ব্যবহার করে এড়িয়ে চলেছি:

for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
  _script_function_call $i;
done;

বর্তমান স্ক্রিপ্ট ফাইলে সন্ধানের আউটপুট পান এবং আপনার ইচ্ছা মতো আউটপুটটি পুনরাবৃত্তি করুন। আমি গৃহীত উত্তরের সাথে একমত নই, তবে আমি আমার স্ক্রিপ্ট ফাইলের বাইরে ফাংশনটি প্রকাশ করতে চাই না।


এর আকার সীমা রয়েছে
রিচার্ড

2

কোনও ফাংশন সেভাবে কার্যকর করা সম্ভব নয় ।

এটি কাটিয়ে উঠতে আপনি নিজের ফাংশনটি শেল স্ক্রিপ্টে রাখতে পারেন এবং এটিকে কল করতে পারেন find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

এখন এটি হিসাবে এটি ব্যবহার করুন:

find . -exec dosomething.sh {} \;

আমার ~ / বিনে আরও ফাইল এড়ানোর চেষ্টা করছিল। ধন্যবাদ যদিও!
alxndr

আমি ডাউনভোটিং বিবেচনা করেছি তবে সমাধানটি নিজেই খারাপ নয়। দয়া করে কেবল সঠিক উদ্ধৃতি ব্যবহার করুন: dosomething $1=> dosomething "$1"এবং আপনার ফাইলটি সঠিকভাবে শুরু করুনfind . -exec bash dosomething.sh {} \;
ক্যামুসেনসি

2

একটি পৃথক ফাংশনে ফাংশনটি রাখুন এবং এটি সম্পাদন করুন find

শেল ফাংশনগুলি শেলটির অভ্যন্তরীণ হয় যার মধ্যে তারা সংজ্ঞায়িত হয়; findএগুলি কখনই দেখতে সক্ষম হবে না।


gotcha; জ্ঞান করে তোলে। যদিও আমার bin / বিনে আরও ফাইল এড়ানোর চেষ্টা করছিল।
alxndr

2

অন্যান্য উত্তরগুলির কয়েকটিতে সংযোজন এবং স্পষ্টতা সরবরাহ করতে, আপনি যদি execবা execdir( -exec command {} +) এর জন্য বাল্ক বিকল্পটি ব্যবহার করে থাকেন এবং সমস্ত অবস্থানগত যুক্তি পুনরুদ্ধার করতে চান তবে আপনার $0সাথে সামঞ্জস্য করার বিষয়টি বিবেচনা করা উচিত bash -c। আরও দৃ concrete়তার সাথে, নীচের কমান্ডটি বিবেচনা করুন, যা bash -cউপরের পরামর্শ অনুযায়ী ব্যবহার করে এবং প্রতিটি ডিরেক্টরি থেকে '.wav' দিয়ে শেষ হওয়া ফাইল পাথগুলি প্রতিধ্বনিত করে:

find "$1" -name '*.wav' -execdir bash -c 'echo $@' _ {} +

বাশ ম্যানুয়ালটি বলে:

If the -c option is present, then commands are read from the first non-option argument command_string.  If there are arguments after the command_string, they  are  assigned  to  the
                 positional parameters, starting with $0.

এখানে, 'check $@' কমান্ড স্ট্রিং এবং _ {}কমান্ড স্ট্রিংয়ের পরে যুক্তি রয়েছে। দ্রষ্টব্য যে $@ব্যাশের একটি বিশেষ অবস্থানিক পরামিতি যা 1 থেকে শুরু করে সমস্ত অবস্থানগত পরামিতিগুলিতে প্রসারিত হয় । এছাড়াও নোট করুন যে -cবিকল্পের সাথে প্রথম যুক্তিটি অবস্থানগত প্যারামিটারে নির্ধারিত হয় $0। এর অর্থ এই যে আপনি যদি অবস্থানগত পরামিতিগুলির সাথে সমস্তটি অ্যাক্সেস করার চেষ্টা করেন তবে আপনি $@কেবলমাত্র পরামিতিগুলি শুরু থেকে শুরু $1করবেন। এই কারণেই ডোমিনিকের উত্তরটিতে _প্যারামিটারটি পূরণ করার জন্য একটি ছদ্মবেশী যুক্তি রয়েছে $0, তাই আমরা চাইলে সমস্ত যুক্তিই পরে ব্যবহার করা যায় যদি আমরা ব্যবহার করি$@ উদাহরণস্বরূপ প্যারামিটার সম্প্রসারণ , বা সেই উত্তর হিসাবে লুপের জন্য।

অবশ্যই, গৃহীত উত্তরের অনুরূপ, bash -c 'shell_function $0 $@'স্পষ্টভাবে পাস দিয়েও কাজ করবে $0, তবে আবারও আপনাকে মনে রাখতে হবে যা $@প্রত্যাশার মতো কাজ করবে না।


0

সরাসরি না, না। অনুসন্ধানটি আপনার শেল নয়, একটি পৃথক প্রক্রিয়াতে কার্যকর করছে।

এমন শেল স্ক্রিপ্ট তৈরি করুন যা আপনার ফাংশনের মতো একই কাজ করে এবং এটি খুঁজে পেতে পারে -exec


আমার ~ / বিনে আরও ফাইল এড়ানোর চেষ্টা করছিল। ধন্যবাদ যদিও!
alxndr

-2

আমি -execপুরোপুরি ব্যবহার এড়ানো হবে । কেন ব্যবহার xargsকরবেন না ?

find . -name <script/command you're searching for> | xargs bash -c

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