সমস্যাটি
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 টি প্রধান ধরণের শেল দেখতে পাই:
যেগুলি এখনও মাল্টি-বাইট সচেতন নয় dash
। তাদের জন্য, একটি বাইট একটি চরিত্রের মানচিত্র। উদাহরণস্বরূপ, ইউটিএফ -8 côté
এ 4 টি অক্ষর, তবে 6 বাইট রয়েছে। এমন একটি লোকালে যেখানে ইউটিএফ -8 চার্সেট, ইন
find . -name '????' -exec dash -c '
name=${1##*/}; echo "${#name}"' sh {} \;
find
যার নামটি ইউটিএফ -8 এ এনকোডযুক্ত 4 টি অক্ষর সমন্বিত ফাইলগুলি সফলভাবে সন্ধান করবে তবে dash
4 এবং 24 এর মধ্যে দৈর্ঘ্যের প্রতিবেদন করবে।
yash
: বিপরীত। এটি কেবল চরিত্রগুলির সাথে সম্পর্কিত । এটি লাগে সমস্ত ইনপুট অভ্যন্তরীণভাবে অক্ষরগুলিতে অনুবাদ করা হয়। এটি সর্বাধিক সামঞ্জস্যপূর্ণ শেল তৈরি করে, তবে এর অর্থ হ'ল এটি নির্বিচারে বাইট সিকোয়েন্সগুলি (যাঁরা বৈধ অক্ষরগুলিতে অনুবাদ করেন না) তাদের সাথে মানিয়ে নিতে পারে না। এমনকি সি লোকালেও 0x7f এর উপরে বাইট মানগুলি সহ্য করতে পারে না।
find . -exec yash -c 'echo "$1"' sh {} \;
কোনও ইউটিএফ -8 স্থানীয় côté.txt
ক্ষেত্রে উদাহরণস্বরূপ আগের থেকে আমাদের আইএসও -8859-1 এ ব্যর্থ হবে ।
যারা পছন্দ করেছেন bash
বা zsh
যেখানে মাল্টি-বাইট সমর্থন ক্রমান্বয়ে যুক্ত করা হয়েছে। এগুলি এমন বাইটগুলি বিবেচনা করে ফিরে আসবে যা চরিত্রগুলিতে ম্যাপ করা যায় না যেমন তারা অক্ষর। তাদের এখনও কিছু বাগ রয়েছে এবং সেখানে বিশেষত জিবিকে বা বিআইজি 5-এইচকেএসসিএসের মতো কম সাধারণ মাল্টি বাইট অক্ষরগুলি রয়েছে (তাদের বেশিরভাগ নোংরা হিসাবে তাদের বহু মাল্টি বাইট অক্ষরগুলিতে 0-127 রেঞ্জের বাইট থাকে (ASCII অক্ষরের মতো) )।
যারা sh
ফ্রিবিএসডি (কমপক্ষে 11 টি) বা mksh -o utf8-mode
এটি মাল্টি-বাইট সমর্থন করে তবে কেবল ইউটিএফ -8 এর জন্য।
নোট
1 সম্পূর্ণতার জন্য, আমরা zsh
স্মরণে পুরো তালিকাটি সংরক্ষণ না করে পুনরাবৃত্ত গ্লোব্বিং ব্যবহার করে ফাইলগুলি লুপ করার জন্য একটি হ্যাকি উপায় উল্লেখ করতে পারি :
process() {
something with $REPLY
false
}
: **/*(ND.m-1+process)
+cmd
একটি গ্লোব যোগ্যতা যা cmd
বর্তমান ফাইল পাথের সাথে কল করে (সাধারণত কোনও ফাংশন) $REPLY
। ফাইলটি নির্বাচন করা উচিত কিনা তা স্থির করতে ফাংশনটি সত্য বা মিথ্যা প্রত্যাবর্তন করে (এবং অ্যারেতে $REPLY
বেশ কয়েকটি ফাইল সংশোধন করতে বা ফিরিয়ে আনতে পারে $reply
)। এখানে আমরা সেই ফাংশনে প্রসেসিং করি এবং মিথ্যা ফিরব যাতে ফাইলটি নির্বাচন করা হয় না।