আউটপুট পার্সিং ls
হয় নির্ভরযোগ্য নয় ।
পরিবর্তে, find
ফাইলগুলি সনাক্ত করতে এবং sort
টাইমস্ট্যাম্পের মাধ্যমে তাদের অর্ডার করতে ব্যবহার করুন। উদাহরণ স্বরূপ:
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
এসব কি করছে?
প্রথমত, find
কমান্ডগুলি বর্তমান ডিরেক্টরি ( .
) এর সমস্ত ফাইল এবং ডিরেক্টরি সনাক্ত করে তবে বর্তমান ডিরেক্টরি ( ) এর উপ-ডিরেক্টরিতে নয় -maxdepth 1
, তারপরে প্রিন্ট করে:
- একটি টাইমস্ট্যাম্প
- একটি স্থান
- ফাইলের আপেক্ষিক পথ
- একটি শূন্য চরিত্র
টাইমস্ট্যাম্প গুরুত্বপূর্ণ। %T@
জন্য বিন্যাস সুনির্দিষ্টভাবে উল্লেখ করা -printf
মধ্যে প্রায় ভেঙে পড়েছে T
, যা ফাইল (র দ্বারা mtime) এবং এর "সর্বশেষ পরিবর্তনের সময়" ইঙ্গিত @
, যা "1970 সাল থেকে সেকেন্ড", ভগ্ন সেকেন্ড সহ নির্দেশ করে।
স্থানটি নিছক একটি স্বেচ্ছাসেবী সীমারেখা। ফাইলটির পুরো পথটি যাতে আমরা পরে এটি উল্লেখ করতে পারি, এবং এনইউএলএল চরিত্রটি একটি টার্মিনেটর কারণ এটি কোনও ফাইলের নামের একটি অবৈধ চরিত্র এবং সুতরাং আমাদের নিশ্চিতভাবে জানতে দেয় যে আমরা পাথের শেষের দিকে পৌঁছেছি ফাইল।
আমি অন্তর্ভুক্ত করেছি 2>/dev/null
যাতে ব্যবহারকারীর অ্যাক্সেসের অনুমতি না থাকা ফাইলগুলি বাদ দেওয়া হয় তবে তাদের বাদ দেওয়া সংক্রান্ত ত্রুটি বার্তাগুলি দমন করা হয়।
find
কমান্ডের ফলাফলটি বর্তমান ডিরেক্টরিতে থাকা সমস্ত ডিরেক্টরিগুলির একটি তালিকা। তালিকাটি পাইপ করা হয়েছে sort
যাতে নির্দেশ দেওয়া হয়:
-z
নিউলাইন পরিবর্তে লাইন টার্মিনেটর চরিত্র হিসাবে NULL আচরণ করুন।
-n
সংখ্যা অনুসারে বাছাই করুন
যেহেতু সেকেন্ড -১ 1970০ since সর্বদা উপরে চলে যায় আমরা সেই ফাইলটি চাই যার টাইমস্ট্যাম্পটি সবচেয়ে ছোট সংখ্যা ছিল। এর থেকে প্রথম ফলাফলটি sort
হবে সর্বনিম্ন সংখ্যাযুক্ত টাইমস্ট্যাম্প যুক্ত লাইন। যা যা অবশিষ্ট রয়েছে তা ফাইলের নাম তোলা।
ফলাফল find
, sort
পাইপলাইন মাধ্যমে পাস করা হয়েছে প্রক্রিয়া প্রতিকল্পন করতে while
যেখানে এটি পড়া হয় যেমন যদি এটা stdin একটি ফাইল ছিল। while
পরিবর্তে read
ইনপুট প্রক্রিয়া করার জন্য অনুরোধ ।
প্রসঙ্গে read
আমরা IFS
ভেরিয়েবলটিকে কিছুতেই সেট করি নি, যার অর্থ হ'ল স্পেস স্পেসটি অনুচিতভাবে ডিলিমিটার হিসাবে ব্যাখ্যা করা হবে না। read
বলা হয় -r
, যা পালানোর প্রসারকে অক্ষম করে এবং -d $'\0'
যা আমাদের find
, sort
পাইপলাইন থেকে আউটপুটটির সাথে মিল রেখে শেষ প্রান্তের ডিলিমিটারকে NULL করে তোলে ।
ডেটার প্রথম খণ্ড, যে প্রাচীনতম ফাইল পাথ তার টাইমস্ট্যাম্প এবং ব্যবধানের দ্বারা পূর্বে প্রতিনিধিত্ব করে, পরিবর্তনশীল মধ্যে পড়া হয় line
। এর পরে, প্যারামিটার প্রতিস্থাপনটি এক্সপ্রেশন দিয়ে ব্যবহৃত হয় #*
, যা স্ট্রিংয়ের শুরু থেকে শুরু করে প্রথম স্থান পর্যন্ত সমস্ত অক্ষরকে কিছু না করে কেবল স্থান দেয়। এটি পরিবর্তনের টাইমস্ট্যাম্পটিকে সরিয়ে দেয়, কেবলমাত্র ফাইলের পুরো পথ ছেড়ে।
এই মুহুর্তে ফাইলের নামটি সংরক্ষণ করা হয় $file
এবং আপনি এটির সাথে নিজের পছন্দ মতো কিছু করতে পারেন। আপনার সাথে কিছু যখন করছেন সমাপ্ত করছি বিবৃতি ইচ্ছা লুপ এবং কমান্ড পরবর্তী খণ্ড এবং পরবর্তী ফাইলের নাম আহরণের আবার মৃত্যুদন্ড কার্যকর করা হবে।$file
while
read
একটি সহজ উপায় আছে না?
না। সহজ উপায়গুলি বগি।
আপনি ব্যবহার করেন তাহলে ls -t
এবং নল head
বা tail
(অথবা কিছু ) আপনি ফাইলের নাম মধ্যে নতুন লাইন দিয়ে ফাইল ভেঙ্গে ফেলবো। যদি আপনি mv $(anything)
নামের পরে সাদা স্থান সহ ফাইলগুলি ভাঙ্গন সৃষ্টি করেন। mv "$(anything)"
তারপরে আপনি যদি নামে নতুন লাইনের লাইন যুক্ত ফাইলগুলি বিরতি সৃষ্টি করে। যদি আপনি তা read
না করে থাকেন -d $'\0'
তবে তাদের নামে সাদা স্থান সহ ফাইলগুলি ভাঙ্গবেন।
সম্ভবত সুনির্দিষ্ট ক্ষেত্রে আপনি নিশ্চিতভাবেই জানেন যে একটি সহজ উপায় যথেষ্ট, তবে আপনি যদি এড়াতে না পারেন তবে স্ক্রিপ্টগুলিতে কখনও এমন অনুমানগুলি লিখতে হবে না।
সমাধান
#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
কল করুন:
move-oldest /mnt/backup/ /var/log/foo/ 20
পুরানো 20 ফাইলগুলিতে স্থানান্তরিত /var/log/foo/
করতে /mnt/backup/
।
মনে রাখবেন যে আমি ফাইল এবং ডিরেক্টরিগুলি অন্তর্ভুক্ত করছি । ফাইলের জন্য শুধুমাত্র যোগ -type f
করার find
আবাহন।
ধন্যবাদ
ধন্যবাদ enzotib এবং Павел Танков এই উত্তর উন্নতি জন্য।