কেন অনুসন্ধানের আউটপুটটি খারাপ অভ্যাসকে ফাঁপা করছে?


170

এই প্রশ্ন দ্বারা অনুপ্রাণিত

পাঠ্যকে প্রক্রিয়াজাত করতে শেল লুপ ব্যবহার করা খারাপ অভ্যাস হিসাবে বিবেচিত হয় কেন?

আমি এই নির্মাণগুলি দেখতে পাচ্ছি

for file in `find . -type f -name ...`; do smth with ${file}; done

এবং

for dir in $(find . -type d -name ...); do smth with ${dir}; done

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

কেন findআউটপুট খারাপ অনুশীলনটি লুপিং করা হচ্ছে এবং প্রতিটি ফাইলের নাম / পাথের জন্য এক বা একাধিক কমান্ড চালানোর উপযুক্ত উপায় কী find?


12
আমি মনে করি এটি "নেভার আউটপুট কখনই পার্স করবেন না!" - আপনি অবশ্যই অফ অফ ভিত্তিতে একটি করতে পারেন, তবে তারা উত্পাদন মানের চেয়ে দ্রুত হ্যাক বেশি। বা, আরও সাধারণভাবে, স্পষ্টতই কখনও মতবাদী হবেন না।
ব্রুস এডিগার


এটি একটি প্রথাগত উত্তরে রূপান্তরিত হওয়া উচিত
জায়েদ

6
কারণ অনুসন্ধানের বিন্দুটি এটি যা খুঁজে পায় তার উপর লুপ করা।
অরেঞ্জডোগ

2
একটি আনুষঙ্গিক বিন্দু - আপনি কোনও ফাইলে আউটপুট প্রেরণ করতে চাইতে পারেন এবং পরে স্ক্রিপ্টে এটি প্রক্রিয়া করতে পারেন। আপনার যদি স্ক্রিপ্টটি ডিবাগ করতে হয় তবে ফাইল তালিকা পর্যালোচনার জন্য উপলব্ধ।
ব্যবহারকারী 117529

উত্তর:


87

সমস্যাটি

for f in $(find .)

দুটি বেমানান জিনিস একত্রিত।

findনিউলাইন অক্ষর দ্বারা বিযুক্ত ফাইল পাথের একটি তালিকা মুদ্রণ করে। আপনি যখন $(find .)সেই তালিকার প্রসঙ্গটি অপ্রকাশিত রেখে যান তখন বিভক্ত + গ্লোব অপারেটরটি যখন অনুরোধ করা হয় তখন এটি ডিফল্টরূপে অক্ষরে আলাদা হয় $IFS(ডিফল্টরূপে নতুন লাইনও অন্তর্ভুক্ত থাকে তবে স্থান এবং ট্যাব (এবং zshএনএলইউ ইন )) থাকে এবং প্রতিটি ফলাফল শব্দের উপরে গ্লোব্বিং সম্পাদন করে (ব্যতীত in zsh) (এবং ksh93 বা pdksh ডেরিভেটিভগুলিতেও ব্রেস সম্প্রসারণ!)।

এমনকি যদি আপনি এটি তৈরি করেন:

IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
              # but not ksh93)
for f in $(find .) # invoke split+glob

এটি এখনও ভুল কারণ নতুন লাইনের চরিত্রটি কোনও ফাইলের পথের মতোই বৈধ। এর আউটপুটটি find -printনির্ভরযোগ্যভাবে পোস্ট-প্রসেসেবল নয় (কিছু বিভ্রান্ত কৌশল ব্যবহার করে, যেমন এখানে দেখানো হয়েছে ) বাদ দিয়ে ।

এর অর্থ হ'ল শেলটি findসম্পূর্ণরূপে আউটপুট সংরক্ষণ করতে হবে এবং তারপরে ফাইলটি লুপ করা শুরু করার আগে এটি বিভক্ত + গ্লোব করুন (যা বোঝায় যে আউটপুটটিকে মেমোরিতে দ্বিতীয়বার স্টোরেজ করে)।

নোটের find . | xargs cmdঅনুরূপ সমস্যা রয়েছে (সেখানে, ফাঁকা, নিউলাইন, একক উক্তি, ডাবল উদ্ধৃতি এবং ব্যাকস্ল্যাশ (এবং কিছু xargবাস্তবায়ন বাইটস সহ বৈধ অক্ষরের অংশ গঠন করে না) সমস্যা রয়েছে)

আরও সঠিক বিকল্প

একমাত্র উপায় ব্যবহার করতে forআউটপুট উপর লুপ findব্যবহার করতে হবে zshযে সমর্থন IFS=$'\0'এবং:

IFS=$'\0'
for f in $(find . -print0)

(প্রতিস্থাপন -print0সঙ্গে -exec printf '%s\0' {} +জন্য findবাস্তবায়নের যে অ-মানক (কিন্তু খুবই সাধারণ আজকাল) সমর্থন করে না -print0)।

এখানে, সঠিক এবং বহনযোগ্য উপায়টি হ'ল -exec:

find . -exec something with {} \;

বা যদি somethingএকাধিক যুক্তি নিতে পারে:

find . -exec something with {} +

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

find . -exec sh -c '
  for file do
    something < "$file"
  done' find-sh {} +

(সাবধান, এটি একের বেশি শুরু হতে পারে sh)।

কিছু সিস্টেমে আপনি ব্যবহার করতে পারেন:

find . -print0 | xargs -r0 something with

যে আদর্শ সিনট্যাক্স উপর সামান্য সুবিধা আছে এবং অর্থ যদিও somethingএর stdinপারেন নল বা /dev/null

আপনি যেটি ব্যবহার করতে চাইতে পারেন তার একটি কারণ সমান্তরাল প্রক্রিয়াকরণের জন্য -Pজিএনইউ বিকল্পটি ব্যবহার করতে পারে xargs। প্রক্রিয়া প্রতিস্থাপনের শাঁস সমর্থন করে বিকল্পটি stdinজিএনইউর সাথেও এই বিষয়টি নিয়ে কাজ করা যেতে পারে :xargs-a

xargs -r0n 20 -P 4 -a <(find . -print0) something

উদাহরণস্বরূপ, somethingপ্রতিটি 20 টি ফাইল আর্গুমেন্ট গ্রহণের 4 টি একযোগে প্রার্থনা চালানো ।

সঙ্গে zshবা bash, আউটপুট উপর লুপ অন্য উপায় find -print0সঙ্গে আছেন

while IFS= read -rd '' file <&3; do
  something "$file" 3<&-
done 3< <(find . -print0)

read -d '' নিউলাইন সীমিত রেকর্ডগুলির পরিবর্তে NUL সীমাবদ্ধ রেকর্ডগুলি পড়ে।

bash-4.4এবং উপরের ফাইলগুলি find -print0অ্যারের দ্বারা ফিরে আসা ফাইলগুলিও সংরক্ষণ করতে পারে :

readarray -td '' files < <(find . -print0)

zshসমতুল্য (যা সংরক্ষণের সুবিধা আছে findএর প্রস্থান অবস্থা):

files=(${(0)"$(find . -print0)"})

এর সাথে zsh, আপনি findগ্লোব কোয়ালিফায়ারগুলির সাথে পুনরাবৃত্ত গ্লোব্বিংয়ের সংমিশ্রণে সর্বাধিক অভিব্যক্তি অনুবাদ করতে পারেন । উদাহরণস্বরূপ, লুপিং find . -name '*.txt' -type f -mtime -1হবে:

for file (./**/*.txt(ND.m-1)) cmd $file

অথবা

for file (**/*.txt(ND.m-1)) cmd -- $file

( --যেমন হিসাবে প্রয়োজন থেকে সাবধান **/*, ফাইল পাথ শুরু হয় না ./, -উদাহরণস্বরূপ শুরু হতে পারে )।

ksh93এবং bashঅবশেষে সমর্থন যোগ করল **/(যদিও পুনরাবৃত্তাকারী গ্লোবিংয়ের আরও অগ্রগতি নয়) তবে এখনও গ্লোব বাছাইকারীরা নয় যা **সেখানে খুব সীমিত ব্যবহারের সুযোগ করে দেয়। এছাড়াও সাবধান থাকুন যে bashডিরেক্টরি ট্রিতে অবতরণ করার পরে 4.3 এর পূর্বে প্রতীকগুলি অনুসরণ করে।

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

অন্যান্য নির্ভরযোগ্যতা / সুরক্ষা বিবেচনা

রেসের অবস্থা

এখন, আমরা যদি নির্ভরযোগ্যতার কথা বলি, আমাদের সময় find/ zshফাইলের সন্ধানের সময় এবং এটি যে মানদণ্ড এবং এটি ব্যবহৃত হচ্ছে তার সময়টি পূরণ করে কিনা তা পরীক্ষা করে দেখতে হবে ( ট্যাকটিউ রেস )।

এমনকি একটি ডিরেক্টরি ট্রি অবতরণ করার সময়ও কাউকে সিমলিংকগুলি অনুসরণ না করা এবং এটি ট্যাকটিউও রেস ছাড়াই না করা নিশ্চিত করতে হবে। find( findকমপক্ষে জিএনইউ ) openat()ডান O_NOFOLLOWপতাকা ব্যবহার করে ডিরেক্টরিগুলি খোলার মাধ্যমে (যেখানে সমর্থিত) এবং প্রতিটি ডিরেক্টরিতে একটি ফাইল বর্ণনাকারী খোলা রেখে zsh/ bash/ তা kshকরবেন না does সুতরাং কোনও আক্রমণকারী সঠিক সময়ে একটি সিমলিংকের সাহায্যে ডিরেক্টরিটি প্রতিস্থাপন করতে সক্ষম হওয়ায় আপনি ভুল ডিরেক্টরিটি অবতরণ করতে পারেন।

এমনকি যদি findডিরেক্টরিটি সঠিকভাবে অবতরণ না করে -exec cmd {} \;এবং আরও অনেক কিছু দিয়ে -exec cmd {} +একবার cmdসম্পাদিত হয়, উদাহরণস্বরূপ cmd ./foo/barবা cmd ./foo/bar ./foo/bar/bazসময় cmdব্যবহারের ফলে ./foo/bar, এর বৈশিষ্ট্যগুলি barমাপদণ্ডগুলির সাথে আর মেলে না find, তবে আরও খারাপ ./fooহতে পারে been অন্য কোনও জায়গায় সিমিলিংক দ্বারা প্রতিস্থাপিত হয়েছে (এবং রেস উইন্ডোটি অনেক বড় হয়ে গেছে -exec {} +যেখানে findকল করার জন্য পর্যাপ্ত ফাইল থাকার অপেক্ষা রয়েছে cmd)।

কিছু findবাস্তবায়নের একটি (অ-মানক এখনও) -execdirদ্বিতীয় সমস্যা উপশম করার পূর্বাভাস রয়েছে।

সঙ্গে:

find . -execdir cmd -- {} \;

find chdir()দৌড়ানোর আগে ফাইলটির মূল ডিরেক্টরিতে প্রবেশ করুন cmd। কল করার পরিবর্তে cmd -- ./foo/bar, এটি কল করে cmd -- ./bar( cmd -- barকিছু বাস্তবায়ন সহ, তাই --), সুতরাং ./fooএকটি সিমিলিংকে পরিবর্তিত হওয়া সমস্যা এড়ানো যায়। এটি সুরক্ষার মতো কমান্ড ব্যবহার করে তোলে rm(এটি এখনও একটি পৃথক ফাইল মুছে ফেলতে পারে, তবে কোনও ভিন্ন ডিরেক্টরিতে কোনও ফাইল নয়), তবে কমান্ডগুলি না যা ফাইলগুলি সংশোধন করতে পারে যদি না তারা সিমলিংক অনুসরণ না করার জন্য ডিজাইন না করে থাকে।

-execdir cmd -- {} +কখনও কখনও এছাড়াও কাজ করে কিন্তু GNU এর কয়েকটি সংস্করণ সহ বেশ কয়েকটি বাস্তবায়ন সহ findএটি সমান -execdir cmd -- {} \;

-execdir খুব গভীর ডিরেক্টরি গাছের সাথে যুক্ত কিছু সমস্যা নিয়ে কাজ করার সুবিধাও রয়েছে।

ইন:

find . -exec cmd {} \;

প্রদত্ত পাথের আকারটি cmdফাইলটি যে ডিরেক্টরিটিতে রয়েছে তার গভীরতার সাথে বাড়বে that যদি সেই আকারটি PATH_MAX(লিনাক্সে 4k এর মতো কিছু) এর চেয়ে বড় হয়ে যায় , তবে cmdসেই পথে যে কোনও সিস্টেম কল ENAMETOOLONGত্রুটিযুক্ত হয়ে ব্যর্থ হবে ।

এর সাথে -execdir, কেবলমাত্র ফাইলের নাম (সম্ভবত উপসর্গযুক্ত ./) এ দেওয়া হয়েছে cmd। বেশিরভাগ ফাইল সিস্টেমে নিজের নামের ফাইলগুলির নামের NAME_MAXচেয়ে অনেক কম সীমা থাকে ( PATH_MAXতাই ) ENAMETOOLONGত্রুটি হওয়ার সম্ভাবনা কম থাকে।

বাইটস বনাম অক্ষর

এছাড়াও, প্রায়শই সুরক্ষার বিষয়টি বিবেচনা করার সময় findএবং সাধারণত সাধারণত ফাইলের নামগুলি হ্যান্ডল করার ক্ষেত্রে অবহেলা করা হয় যে ইউনিক্সের মতো বেশিরভাগ সিস্টেমে ফাইলের নামগুলি বাইটের ক্রম হয় (কোনও ফাইলের পথে বাইট মান ছাড়া 0, এবং বেশিরভাগ সিস্টেমে ( এএসসিআইআই ভিত্তিক একটিগুলি, আমরা আপাতত বিরল EBCDIC ভিত্তিকগুলি উপেক্ষা করব) 0x2f হল পথ সীমানাঙ্কক)।

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

এর অর্থ হ'ল একটি প্রদত্ত ফাইলের নামের লোকেলের উপর নির্ভর করে আলাদা আলাদা পাঠ্য উপস্থাপনা থাকতে পারে। উদাহরণস্বরূপ, বাইট সিকোয়েন্সটি 63 f4 74 e9 2e 74 78 74এমন côté.txtকোনও অ্যাপ্লিকেশনের জন্য যা ফাইলের নামের সাথে স্থানীয়ভাবে অক্ষর সেটটি আইএসও cєtщ.txt-8859-1 রয়েছে এবং সেই জায়গায় অক্ষরটি IS0-8859-5 হয় এমন একটি লোকালয়ে ব্যাখ্যা করা হবে।

খারাপ। একটি স্থানীয় যেখানে চরসেটটি ইউটিএফ -8 (আজকাল প্রচলিত আদর্শ), 63 টি 4৪ 74 ই9 2 ই 78 78 74 কেবল অক্ষরগুলিতে ম্যাপ করা যায়নি!

findএটি হ'ল একটি অ্যাপ্লিকেশন যা ফাইলের নামগুলি তার -name/ -pathপূর্বাভাসের জন্য পাঠ্য হিসাবে বিবেচনা করে (এবং আরও কিছু, যেমন -inameবা -regexকিছু প্রয়োগের সাথে)।

এর অর্থ হ'ল উদাহরণস্বরূপ, বেশ কয়েকটি findবাস্তবায়ন (জিএনইউ সহ find)।

find . -name '*.txt'

63 f4 74 e9 2e 74 78 74কোনও UTF-8 লোকালে ডাকলে আমাদের ফাইলটি উপরের সন্ধান করবে না *(যা 0 বা ততোধিক অক্ষরের সাথে মেলে, বাইট নয়) এই অক্ষরগুলির সাথে মেলে না।

LC_ALL=C find... সি লোকেল চরিত্র অনুসারে একটি বাইট বোঝায় এবং (সাধারণত) গ্যারান্টি দেয় যে সমস্ত বাইট মান একটি অক্ষরে মানচিত্র দেয় (যদিও কিছু বাইট মানের জন্য সম্ভবত অপরিবর্তিত আছে) guaran

এখন যখন শেল থেকে এই ফাইলগুলির নামগুলি লুপিংয়ের কথা আসে তখন বাইট বনাম চরিত্রটিও সমস্যা হয়ে উঠতে পারে। আমরা সাধারণত এই বিষয়ে 4 টি প্রধান ধরণের শেল দেখতে পাই:

  1. যেগুলি এখনও মাল্টি-বাইট সচেতন নয় dash। তাদের জন্য, একটি বাইট একটি চরিত্রের মানচিত্র। উদাহরণস্বরূপ, ইউটিএফ -8 côtéএ 4 টি অক্ষর, তবে 6 বাইট রয়েছে। এমন একটি লোকালে যেখানে ইউটিএফ -8 চার্সেট, ইন

    find . -name '????' -exec dash -c '
      name=${1##*/}; echo "${#name}"' sh {} \;
    

    findযার নামটি ইউটিএফ -8 এ এনকোডযুক্ত 4 টি অক্ষর সমন্বিত ফাইলগুলি সফলভাবে সন্ধান করবে তবে dash4 এবং 24 এর মধ্যে দৈর্ঘ্যের প্রতিবেদন করবে।

  2. yash: বিপরীত। এটি কেবল চরিত্রগুলির সাথে সম্পর্কিত । এটি লাগে সমস্ত ইনপুট অভ্যন্তরীণভাবে অক্ষরগুলিতে অনুবাদ করা হয়। এটি সর্বাধিক সামঞ্জস্যপূর্ণ শেল তৈরি করে, তবে এর অর্থ হ'ল এটি নির্বিচারে বাইট সিকোয়েন্সগুলি (যাঁরা বৈধ অক্ষরগুলিতে অনুবাদ করেন না) তাদের সাথে মানিয়ে নিতে পারে না। এমনকি সি লোকালেও 0x7f এর উপরে বাইট মানগুলি সহ্য করতে পারে না।

    find . -exec yash -c 'echo "$1"' sh {} \;
    

    কোনও ইউটিএফ -8 স্থানীয় côté.txtক্ষেত্রে উদাহরণস্বরূপ আগের থেকে আমাদের আইএসও -8859-1 এ ব্যর্থ হবে ।

  3. যারা পছন্দ করেছেন bashবা zshযেখানে মাল্টি-বাইট সমর্থন ক্রমান্বয়ে যুক্ত করা হয়েছে। এগুলি এমন বাইটগুলি বিবেচনা করে ফিরে আসবে যা চরিত্রগুলিতে ম্যাপ করা যায় না যেমন তারা অক্ষর। তাদের এখনও কিছু বাগ রয়েছে এবং সেখানে বিশেষত জিবিকে বা বিআইজি 5-এইচকেএসসিএসের মতো কম সাধারণ মাল্টি বাইট অক্ষরগুলি রয়েছে (তাদের বেশিরভাগ নোংরা হিসাবে তাদের বহু মাল্টি বাইট অক্ষরগুলিতে 0-127 রেঞ্জের বাইট থাকে (ASCII অক্ষরের মতো) )।

  4. যারা shফ্রিবিএসডি (কমপক্ষে 11 টি) বা mksh -o utf8-modeএটি মাল্টি-বাইট সমর্থন করে তবে কেবল ইউটিএফ -8 এর জন্য।

নোট

1 সম্পূর্ণতার জন্য, আমরা zshস্মরণে পুরো তালিকাটি সংরক্ষণ না করে পুনরাবৃত্ত গ্লোব্বিং ব্যবহার করে ফাইলগুলি লুপ করার জন্য একটি হ্যাকি উপায় উল্লেখ করতে পারি :

process() {
  something with $REPLY
  false
}
: **/*(ND.m-1+process)

+cmdএকটি গ্লোব যোগ্যতা যা cmdবর্তমান ফাইল পাথের সাথে কল করে (সাধারণত কোনও ফাংশন) $REPLY। ফাইলটি নির্বাচন করা উচিত কিনা তা স্থির করতে ফাংশনটি সত্য বা মিথ্যা প্রত্যাবর্তন করে (এবং অ্যারেতে $REPLYবেশ কয়েকটি ফাইল সংশোধন করতে বা ফিরিয়ে আনতে পারে $reply)। এখানে আমরা সেই ফাংশনে প্রসেসিং করি এবং মিথ্যা ফিরব যাতে ফাইলটি নির্বাচন করা হয় না।


Zsh এবং ব্যাশ পাওয়া যায়, তাহলে আপনি পারে ভাল হতে মাত্র globbing এবং শেল নির্মান ব্যবহার পরিবর্তে কোঁচকানো বের করার চেষ্টা findনিরাপদে আচরণ মধ্যে। গ্লোববিং ডিফল্টরূপে সুরক্ষিত থাকে যখন ডিফল্টরূপে অনিরাপদ থাকে।
কেভিন

কেভিন, সম্পাদনা দেখুন।
স্টাফেন চেজেলাস

182

কেন findআউটপুট খারাপ অনুশীলন লুপিং ?

সহজ উত্তরটি হ'ল:

কারণ ফাইলের নামগুলিতে যে কোনও অক্ষর থাকতে পারে ।

অতএব, কোনও মুদ্রণযোগ্য অক্ষর নেই যা আপনি নির্ভরযোগ্যভাবে ফাইলের নামগুলি সীমিত করতে ব্যবহার করতে পারেন।


নতুন লাইন হয় প্রায়ই , ফাইলের নাম সীমানা করার জন্য (ভুল) ব্যবহার কারণ এটি অস্বাভাবিক ফাইলের নামের মধ্যে সম্পর্কে newline অক্ষর অন্তর্ভুক্ত করা।

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

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

যাইহোক, যদি সঠিক, কোডের শক্তসমর্থ সংস্করণ এছাড়াও পড়তে সহজ, সেখানে কোডটি প্রান্ত ক্ষেত্রে ব্যর্থ লেখার জন্য কোন অজুহাত নেই। এটি একই ক্ষেত্রে findএবং পাওয়া প্রতিটি ফাইলের একটি কমান্ড চালানোর প্রয়োজন।


আসুন আরও নির্দিষ্ট করা যাক: ইউনিক্স বা লিনাক্স সিস্টেমে ফাইলের নামগুলিতে একটি /(যা পাথ উপাদান বিভাজক হিসাবে ব্যবহৃত হয়) বাদে কোনও অক্ষর থাকতে পারে এবং সেগুলিতে নাল বাইট নাও থাকতে পারে।

নাল বাইট হ'ল ফাইল নামগুলি সীমাবদ্ধ করার একমাত্র সঠিক উপায়।


যেহেতু জিএনইউতে findএকটি -print0প্রাথমিক অন্তর্ভুক্ত রয়েছে যা প্রিন্ট করা ফাইলের নামগুলি ডিলিট করতে নাল বাইট ব্যবহার করবে, তাই আউটপুট পরিচালনা করতে জিএনইউ এবং এর পতাকা (এবং পতাকা) দিয়ে নিরাপদে জিএনইউ ব্যবহার করা find যেতে পারে :xargs-0-rfind

find ... -print0 | xargs -r0 ...

তবে এই ফর্মটি ব্যবহার করার কোনও ভাল কারণ নেই , কারণ:

  1. এটি জিএনইউ অনুসন্ধানের উপর নির্ভরতা যুক্ত করে যা সেখানে থাকার দরকার নেই এবং
  2. findএটি অনুসন্ধান করা ফাইলগুলিতে কমান্ড চালাতে সক্ষম হওয়ার জন্য ডিজাইন করা হয়েছে

এছাড়াও, জিএনইউ xargsপ্রয়োজন -0এবং -rযেখানে ফ্রিবিএসডি xargsকেবল প্রয়োজন -0(এবং এর কোনও -rবিকল্প নেই), এবং কিছু কিছু xargsসমর্থন -0করে না। সুতরাং কেবল পসিক্স বৈশিষ্ট্যগুলি find(পরবর্তী বিভাগটি দেখুন) আটকে থাকা এবং এড়িয়ে যাওয়া ভাল xargs

যে পয়েন্ট 2 findএর যে ফাইলগুলি খুঁজে পায় তাতে কমান্ড চালানোর দক্ষতার বিষয়ে — আমার মনে হয় মাইক লুকাইডস এটি সেরা বলেছেন:

findএর ব্যবসা এক্সপ্রেশন মূল্যায়ন করছে - ফাইলগুলি সনাক্ত করছে না। হ্যাঁ, findঅবশ্যই ফাইলগুলি সনাক্ত করে; তবে এটি আসলে একটি পার্শ্ব প্রতিক্রিয়া।

- ইউনিক্স পাওয়ার সরঞ্জামসমূহ


POSIX এর নির্দিষ্ট ব্যবহারগুলি find

প্রতিটি findফলাফলের জন্য এক বা একাধিক কমান্ড চালানোর উপযুক্ত উপায় কী ?

পাওয়া প্রতিটি ফাইলের জন্য একটি একক কমান্ড চালাতে, ব্যবহার করুন:

find dirname ... -exec somecommand {} \;

পাওয়া প্রতিটি ফাইলের অনুক্রমে একাধিক কমান্ড চালানোর জন্য, যেখানে প্রথম কমান্ডটি সফল হলে দ্বিতীয় কমান্ডটি চালানো উচিত:

find dirname ... -exec somecommand {} \; -exec someothercommand {} \;

একাধিক ফাইলে একবারে একক কমান্ড চালাতে:

find dirname ... -exec somecommand {} +

find আমি তাল মিলাতে চেষ্টা করছি sh

আপনার যদি কমান্ডের শেল বৈশিষ্ট্যগুলি যেমন আউটপুটটিকে পুনর্নির্দেশ করা বা ফাইলের নাম বা কোনও অনুরূপ কোনও এক্সটেনশন ছড়িয়ে দেওয়ার প্রয়োজন হয় তবে আপনি sh -cকনস্ট্রাক্টটি ব্যবহার করতে পারেন । আপনার এই সম্পর্কে কয়েকটি জিনিস জানা উচিত:

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

  • {}একাধিকবার ব্যবহার করবেন না , বা এটি দীর্ঘতর যুক্তির অংশ হিসাবে ব্যবহার করবেন না । এটি পোর্টেবল নয়। উদাহরণস্বরূপ, এটি করবেন না:

    find ... -exec cp {} somedir/{}.bak \;

    এর জন্য পসিক্স বিশেষ উল্লেখগুলিfind উদ্ধৃত করতে :

    যদি কোনও ইউটিলিটি_নাম বা আর্গুমেন্ট স্ট্রিংয়ে দুটি অক্ষর "{}" থাকে তবে কেবলমাত্র দুটি অক্ষর "{not" নয়, এটি কার্যকরকরণ -সংজ্ঞায়িত করা হয় যে অনুসন্ধানটি দুটি অক্ষরকে প্রতিস্থাপন করে বা পরিবর্তন ছাড়াই স্ট্রিংটি ব্যবহার করে কিনা ।

    ... দুটি অক্ষর "{}" ধারণকারী একাধিক যুক্তি উপস্থিত থাকলে, আচরণটি অনির্ধারিত।

  • -cঅপশনটিতে শেল কমান্ড স্ট্রিংয়ের পরে যুক্তিগুলি আর্গুমেন্টগুলি শেলটির অবস্থানগত পরামিতিগুলিতে সেট করা থাকে, এর সাথে শুরু করে$0 । দিয়ে শুরু হচ্ছে না $1

    এই কারণে, একটি "ডামি" $0মান অন্তর্ভুক্ত করা ভাল , যেমন find-sh, তৈরি হওয়া শেল থেকে ত্রুটি প্রতিবেদন করার জন্য ব্যবহৃত হবে। এছাড়াও, "$@"এটি শেলটিতে একাধিক ফাইল পাস করার সময় কনস্ট্রাক্টসের ব্যবহারের অনুমতি দেয় , যখন একটি মান বাদ দেওয়ার $0অর্থ প্রথম পাস করা ফাইল সেট হয়ে যায় $0এবং সুতরাং এতে অন্তর্ভুক্ত থাকে না "$@"


প্রতি ফাইল একক শেল কমান্ড চালাতে, ব্যবহার করুন:

find dirname ... -exec sh -c 'somecommandwith "$1"' find-sh {} \;

তবে এটি শেল লুপে ফাইলগুলি হ্যান্ডেল করার জন্য আরও ভাল পারফরম্যান্স দেবে যাতে আপনি পাওয়া প্রতিটি ফাইলের জন্য শেল না ছুঁড়ে থাকেন:

find dirname ... -exec sh -c 'for f do somecommandwith "$f"; done' find-sh {} +

(দ্রষ্টব্য যে প্রতিটি অবস্থানগত পরামিতিগুলির for f doসমতুল্য for f in "$@"; doএবং পরিচালনা করে - অন্য কথায়, এটি findতাদের নামের কোনও বিশেষ অক্ষর নির্বিশেষে খুঁজে পাওয়া প্রতিটি ফাইলই ব্যবহার করে ))


সঠিক findব্যবহারের আরও উদাহরণ :

(দ্রষ্টব্য: এই তালিকাটি প্রসারিত নির্দ্বিধায়।)


5
একটি কেস আছে যেখানে আমি findআউটপুট পার্সিংয়ের বিকল্পের কথা জানি না - যেখানে প্রতিটি ফাইলের জন্য আপনাকে বর্তমান শেলটিতে কমান্ড চালাতে হবে (যেমন আপনি ভেরিয়েবল সেট করতে চান)। এই ক্ষেত্রে, while IFS= read -r -u3 -d '' file; do ... done 3< <(find ... -print0)আমি জানি সবচেয়ে ভাল প্রতিমা। দ্রষ্টব্য: <( )বহনযোগ্য নয় - ব্যাশ বা zsh ব্যবহার করুন। এছাড়াও, -u3এবং 3<ভিতরে লুপ stdin পড়া করার চেষ্টা করে যদি কিছু আছে।
গর্ডন ডেভিসন

1
@ গর্ডন ডেভিসন, সম্ভবত — তবে আপনাকে কীসের জন্য এই ভেরিয়েবলগুলি সেট করতে হবে ? আমি হয় যাই হোক না কেন এটা নিয়ে নাড়াচাড়া করতে হবে যে তর্ক করবে ভিতরেfind ... -exec কল। বা কেবল একটি শেল গ্লোব ব্যবহার করুন, যদি এটি আপনার ব্যবহারের ক্ষেত্রে পরিচালনা করে।
ওয়াইল্ডকার্ড

1
আমি প্রায়শই ফাইলগুলি প্রক্রিয়া করার পরে একটি সংক্ষিপ্ত মুদ্রণ করতে চাই ("2 রূপান্তরিত, 3 এড়িয়ে গেছেন, নিম্নলিখিত ফাইলগুলিতে ত্রুটি ছিল: ..."), এবং সেইগুলি গণনা / তালিকাগুলি শেল ভেরিয়েবলগুলিতে জমা করতে হবে। এছাড়াও, এমন পরিস্থিতি রয়েছে যেখানে আমি ফাইলের নামের একটি অ্যারে তৈরি করতে চাই যাতে আমি ক্রমে পুনরাবৃত্তির চেয়ে আরও জটিল জিনিসগুলি করতে পারি (সে ক্ষেত্রে এটিই filelist=(); while ... do filelist+=("$file"); done ...)।
গর্ডন ডেভিসন

3
আপনার উত্তরটি সঠিক। তবে আমি ডগমা পছন্দ করি না। যদিও আমি আরও ভাল জানি, এমন অনেকগুলি (বিশেষত ইন্টারেক্টিভ) ব্যবহারের ক্ষেত্রে রয়েছে যেখানে নিরাপদ এবং findআউটপুট থেকে লুপিং টাইপ করা সহজতর বা আরও খারাপ ব্যবহার ls। আমি সমস্যা ছাড়াই প্রতিদিন এটি করছি। আমি প্রিন্ট 0, --null, -z বা -0 সকল ধরণের সরঞ্জামের বিকল্প সম্পর্কে জানি। তবে সত্যই দরকার না হলে আমি এগুলি আমার ইন্টারেক্টিভ শেল প্রম্পটে ব্যবহার করতে সময় নষ্ট করব না। এটি আপনার উত্তরেও লক্ষ করা যেতে পারে।
rudimeier

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

10

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

সমান্তরালতা এবং স্মৃতি ব্যবহার

প্রদত্ত অন্যান্য উত্তরগুলি বাদ দিয়ে, বিচ্ছেদ সমস্যা এবং এর সাথে সম্পর্কিত, এর সাথে আরও একটি সমস্যা রয়েছে

for file in `find . -type f -name ...`; do smth with ${file}; done

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

পছন্দসই ইউনিক্সের উপায় হ'ল পাইপগুলির সাথে কাজ করা, যা অন্তর্নিহিতভাবে সমান্তরালভাবে চলছে, এবং সাধারণভাবে যথেচ্ছভাবে বিশাল বাফারগুলির প্রয়োজন হয় না। এর অর্থ: আপনি findআপনার সমান্তরালভাবে চলমানটিকে পছন্দ করতে পছন্দ করবেন smthএবং কেবলমাত্র বর্তমান ফাইলের নামটি র‍্যামে রাখুন যখন এটি বন্ধ হয়ে যায় smth

তার জন্য একটি অন্ততপক্ষে আংশিক Okish সমাধানটি পূর্বোক্ত find -exec smth। এটি সমস্ত ফাইলের নাম স্মৃতিতে রাখার প্রয়োজনীয়তা সরিয়ে দেয় এবং সমান্তরালে সুন্দরভাবে চালিত হয়। দুর্ভাগ্যক্রমে, এটি smthফাইলের জন্যও একটি প্রক্রিয়া শুরু করে । যদি smthকেবলমাত্র একটি ফাইলে কাজ করতে পারে তবে তা এভাবেই হয়।

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

যদি এটি সম্ভব না হয়, তবে find -print0 | xargs -0 smthসম্ভবত এটিই আরও ভাল সমাধান। মন্তব্যে @ ডেভ_থমপসন ৮৮৮ হিসাবে উল্লিখিত হিসাবে, সিস্টেম সীমাটি পৌঁছে গেলে (ডিফল্টরূপে, ১২৮ কিলোবাইটের মধ্যে বা সিস্টেমে যে কোনও সীমা আরোপিত হয় ) এর xargsএকাধিক রান জুড়ে যুক্তিগুলি বিভক্ত করে তোলে এবং কতটি প্রভাবিত করার বিকল্প রয়েছে ফাইলগুলির একটি কলকে দেওয়া হয় , সুতরাং প্রক্রিয়া সংখ্যা এবং প্রাথমিক বিলম্বের মধ্যে ভারসাম্য খুঁজে পাওয়া যায় ।smthexecsmthsmth

সম্পাদনা: "সেরা" এর ধারণাগুলি সরিয়ে নিয়েছে - আরও ভাল কিছু উত্পন্ন হবে কিনা তা বলা শক্ত। ;)


find ... -exec smth {} +সমাধান হয়।
ওয়াইল্ডকার্ড

find -print0 | xargs smthমোটেও কাজ করে না, তবে find -print0 | xargs -0 smth(দ্রষ্টব্য -0) অথবা find | xargs smthযদি ফাইলের নামগুলিতে শ্বেতক্ষেত্রের উদ্ধৃতি বা ব্যাকস্ল্যাশ না থাকে তবে smthউপলব্ধ ফাইল ফাইলের একটি এবং একটি যুক্তি তালিকায় ফিট করে ; যদি আপনি ম্যাক্সার্যাগস অতিক্রম করেন তবে প্রদত্ত সমস্ত অর্গগুলি smthহ্যান্ডেল করার জন্য এটি প্রয়োজনীয় হিসাবে বহুবার চলে (কোনও সীমা নেই)। আপনি এর সাথে আরও ছোট 'খণ্ড' সেট করতে পারেন (এভাবে কিছুটা আগে সমান্তরালতা) -L/--max-lines -n/--max-args -s/--max-chars
dave_thompson_085


4

একটি কারণ হ'ল হোয়াইটস্পেস কাজগুলিতে একটি স্প্যানার ছুড়ে দেয়, ফলে ফাইল 'foo বার' 'foo' এবং 'বার' হিসাবে মূল্যায়ন হয়।

$ ls -l
-rw-rw-r-- 1 ec2-user ec2-user 0 Nov  7 18:24 foo bar
$ for file in `find . -type f` ; do echo filename $file ; done
filename ./foo
filename bar
$

এর পরিবর্তে -exec ব্যবহৃত হলে ঠিক আছে

$ find . -type f -exec echo filename {} \;
filename ./foo bar
$ find . -type f -exec stat {} \;
  File: ‘./foo bar’
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: ca01h/51713d    Inode: 9109        Links: 1
Access: (0664/-rw-rw-r--)  Uid: (  500/ec2-user)   Gid: (  500/ec2-user)
Access: 2016-11-07 18:24:42.027554752 +0000
Modify: 2016-11-07 18:24:42.027554752 +0000
Change: 2016-11-07 18:24:42.027554752 +0000
 Birth: -
$

বিশেষত findযেহেতু প্রতিটি ফাইলে একটি কমান্ড কার্যকর করার বিকল্প রয়েছে এটি সহজেই সেরা বিকল্প।
সেন্টেমানি

1
-exec ... {} \;বনাম বিবেচনা করুন-exec ... {} +
থ্র্যাগ করুন

1
যদি আপনি ব্যবহার করেন for file in "$(find . -type f)" এবং echo "${file}"এটি এমনকি
শ্বেত স্পেসগুলির

9
@ মমস - না, উদ্ধৃতি আপনি যা ভাবেন তা করে না। একাধিক ফাইল সহ একটি ডিরেক্টরিতে চেষ্টা করুন for file in "$(find . -type f)";do printf '%s %s\n' name: "${file}";doneযা (আপনার মতে) প্রতিটি ফাইলের নাম পৃথক লাইনে প্রিন্ট করা উচিত name:। এটা হয় না।
don_crissti

2

কারণ যে কোনও কমান্ডের আউটপুট একটি একক স্ট্রিং, তবে আপনার লুপটি লুপ করতে একটি স্ট্রিংয়ের অ্যারের প্রয়োজন। এটি "কাজ" করার কারণটি হ'ল শেলগুলি বিশ্বাসঘাতকতার সাথে আপনার জন্য সাদা স্থানের স্ট্রিংটি বিভক্ত করে।

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

বাশ উদাহরণ:

shopt -s nullglob globstar
for i in **
do
    echo «"$i"»
done

ফিশেও একই:

for i in **
    echo «$i»
end

আপনার যদি এর বৈশিষ্ট্যগুলির প্রয়োজন হয় তবে findকেবল নুলিতে বিভক্ত হওয়া নিশ্চিত করুন (যেমন find -print0 | xargs -r0প্রতিমা)।

মাছগুলি NUL সীমাবদ্ধ আউটপুট পুনরাবৃত্তি করতে পারে। সুতরাং এটি আসলে খারাপ নয় :

find -print0 | while read -z i
    echo «$i»
end

সর্বশেষ ছোট গোচা হিসাবে, অনেকগুলি শেলের মধ্যে (অবশ্যই ফিশ নয়) কমান্ড আউটপুটকে লুপ করা লুপের বডিটিকে একটি সাবশেল করে তোলে (যার অর্থ আপনি লুপটি শেষ হওয়ার পরে দৃশ্যমান কোনও উপায়ে ভেরিয়েবল সেট করতে পারবেন না) যা হ'ল আপনি কি চান না।


নিবন্ধন করুন এটি সাধারণত কাজ করে না । আমি এটি "কাজ করে" (উদ্ধৃতি সহ) এই বলে ব্যঙ্গাত্মক হওয়ার চেষ্টা করছিলাম।
ব্যবহারকারী 2394284

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

1

সন্ধানের আউটপুটটি লুপ করা খারাপ অভ্যাস নয় — কোন খারাপ অভ্যাসটি (এই এবং সমস্ত পরিস্থিতিতে) ধরে নিচ্ছে যে আপনার ইনপুটটি একটি নির্দিষ্ট ফর্ম্যাটটি জানার পরিবর্তে (পরীক্ষা ও নিশ্চিতকরণ) একটি নির্দিষ্ট বিন্যাস।

tldr / সিবিএফ: find | parallel stuff

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