পুরানো ফাইলগুলি সরানোর জন্য শেল স্ক্রিপ্ট?


14

একমাত্র 20 টি পুরানো ফাইলগুলি একটি ফোল্ডার থেকে অন্য ফোল্ডারে স্থানান্তর করার জন্য আমি কীভাবে স্ক্রিপ্ট লিখব? কোনও ফোল্ডারে পুরানো ফাইলগুলি ধরার কোনও উপায় আছে কি?


উপ-ডিরেক্টরি অন্তর্ভুক্ত বা বাদ? এবং এটি কি পুনরাবৃত্তভাবে করা উচিত (ডিরেক্টরি ট্রিতে)?
maxschlepzig

2
অনেক (সর্বাধিক?) * নিক্স ফাইল সিস্টেমগুলি তৈরির তারিখ সংরক্ষণ করে না, তাই আপনি সুনির্দিষ্টতার সাথে প্রাচীনতম ফাইলটি নির্ধারণ করতে পারবেন না । সাধারণত উপলভ্য বৈশিষ্ট্যগুলি হ'ল atime(সর্বশেষ অ্যাক্সেস), ctime(শেষ অনুমতি পরিবর্তন) এবং mtime(সর্বশেষ পরিবর্তিত) ... যেমন। ls -tএবং খোদার এই printf "%T" ব্যবহার mtime... মনে হচ্ছে অনুযায়ী এই লিঙ্কে , যে আমার ext4পার্টিশন হয় সক্ষম একটি সৃষ্টি তারিখ পরিচালনার কিন্তু lsএবং findএবং statউপযুক্ত বিকল্প ব্যবহার করতে পারেন না (এখনও) ...
Peter.O

উত্তর:


13

আউটপুট পার্সিং 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এবং আপনি এটির সাথে নিজের পছন্দ মতো কিছু করতে পারেন। আপনার সাথে কিছু যখন করছেন সমাপ্ত করছি বিবৃতি ইচ্ছা লুপ এবং কমান্ড পরবর্তী খণ্ড এবং পরবর্তী ফাইলের নাম আহরণের আবার মৃত্যুদন্ড কার্যকর করা হবে।$filewhileread

একটি সহজ উপায় আছে না?

না। সহজ উপায়গুলি বগি।

আপনি ব্যবহার করেন তাহলে 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 এবং Павел Танков এই উত্তর উন্নতি জন্য।


সাজানোর ব্যবহার করা উচিত নয় -n। কমপক্ষে আমার সংস্করণে এটি দশমিক সংখ্যাগুলি সঠিকভাবে সাজান না। হয় আপনি তারিখে বা ব্যবহারের বিন্দুটি সরিয়ে ফেলতে হবে -printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz, আইএসও তারিখগুলি বা অন্য কিছু।
l0b0

@ l0b0: এই সীমাবদ্ধতা আমার জানা। আমি অনুমান এটি যথেষ্ট (যে, তার পরেও বাছাই গ্র্যানুলারিটি যে স্তর প্রয়োজন হয় না করতে .আপনাকে অপ্রাসঙ্গিক হতে হবে।) এটা বলা পরিষ্কার হবে sort -z -n -t. -k1
সোর্পিগাল

@ l0b0: আপনার সমাধানটি একই বাগটি প্রদর্শন করে, নির্বিশেষে: %TSএছাড়াও "ভগ্নাংশের অংশ" দেখায় যা ফর্মে থাকবে 00.0000000000, তাই আপনি গ্রানুলারিটিও হারাবেন। সাম্প্রতিক জিএনইউ "সংস্করণ সাজানোর" জন্য sortএই সমস্যাটি সমাধান করতে পারে -V, যা প্রত্যাশার মতো এই ধরণের ভাসমান পয়েন্টটি পরিচালনা করবে।
Sorpigal

না, কারণ আমি সংখ্যার বাছাইয়ের পরিবর্তে "YYYY-MM-DDThh: mm: ss" এ স্ট্রিং বাছাই করি । স্ট্রিং বাছাই দশমিকের জন্য চিন্তা করে না, সুতরাং এটি 10000 বছর পর্যন্ত কাজ করা উচিত :)
l0b0

@ l0b0: একটি স্ট্রিং বাছাই %T@এছাড়াও কাজ করবে, কারণ এটি শূন্য প্যাডযুক্ত।
Sorpigal

4

এটি zsh এর মধ্যে সবচেয়ে সহজ, যেখানে আপনি Om গ্লোব কোয়ালিফায়ার ব্যবহার করে তারিখ অনুসারে ম্যাচগুলি বাছাই করতে (প্রথমতম পুরানো) এবং [1,20]কেবল প্রথম 20 ম্যাচ ধরে রাখতে বাছাইযোগ্য:

mv -- *(Om[1,20]) target/

Dআপনি যদি ডট ফাইলগুলিও অন্তর্ভুক্ত করতে চান তবে যোগ্যতা যুক্ত করুন । .আপনি কেবল নিয়মিত ফাইলগুলি না শুধুমাত্র ডিরেক্টরিতে চাইলে যুক্ত করুন ।

যদি আপনার কাছে জ্যাশ না থাকে তবে এখানে পার্ল ওয়ান-লাইনার রয়েছে (আপনি এটি 80 টিরও কম অক্ষরে করতে পারেন তবে আরও ব্যয় করে স্পষ্টতার সাথে):

perl -e '@files = sort {-M $b <=> -M $a} glob("*"); foreach (@files[0..1]) {rename $_, "target/$_" or die "$_: $!"}'

কেবলমাত্র পসিক্স সরঞ্জাম বা বাশ বা কেএসএস দিয়ে, তারিখ অনুসারে ফাইলগুলি বাছাই করা একটি ব্যথা। আপনি এটি দিয়ে সহজেই করতে পারেন lsতবে এর আউটপুট পার্সিং lsকরা সমস্যাযুক্ত, তাই কেবলমাত্র যদি ফাইলের নামগুলিতে নিউলাইনগুলি ব্যতীত অন্য মুদ্রণযোগ্য অক্ষর থাকে তবে এটি কাজ করে।

ls -tr | head -n 20 | while IFS= read -r file; do mv -- "$file" target/; done

4

বা ls -tসাথে আউটপুট একত্রিত করুন ।tailhead

সরল উদাহরণ, যা কেবলমাত্র যদি সমস্ত ফাইলের নামগুলিতে কেবল সাদা বাক্স এবং \[*?:

 mv $(ls -1tr | head -20) other_folder

1
ls -1Atr
L-এ

1
-1, বিপজ্জনক। এখানে আমাকে একটি উদাহরণ নৈপুণ্য যাক: touch $'foo\n*'। আপনি যদি সেখানে বসে সেই ফাইলটির সাথে এমভি "$ (এলএস)" চালান তবে কী হবে?
Sorpigal

1
@ সরপিগল সিরিয়াসলি? এটা বলা দুর্বল ধরনের "আমাকে একটি উদাহরণ আপনি নির্দিষ্টভাবে বলেন সঙ্গে আসা পর্যন্ত দেওয়া হবে না কাজ আরে লুক, এটা আছে কাজ নয়।"
মাইকেল Mrozek

1
@ সর্পিগাল এটি কোনও খারাপ ধারণা নয়, এটি 99% ক্ষেত্রে কাজ করে। উত্তরটি হ'ল "যদি আপনার কাছে সাধারণ নামযুক্ত ফাইল থাকে তবে এটি কাজ করে। আপনি যদি একটি উন্মাদ ব্যক্তি হন যারা তাদের ফাইল নামগুলিতে নতুন লাইনে এম্বেড করেন তবে তা হবে না"। এটি সম্পূর্ণ সঠিক
মাইকেল মরোজেক

1
@ মিশেলমরোজেক: এটি একটি খারাপ ধারণা এবং এটি খারাপ কারণ এটি কখনও কখনও ব্যর্থ হয়। যদি কখনও কখনও যা ব্যর্থ হয় এবং যা না ঘটে তার করার বিকল্প যদি আপনার কাছে থাকে তবে আপনার উচিত এমন বিকল্পটি গ্রহণ করা উচিত যা না হয় (এবং এটি যেটি খারাপ তা)। আপনি ইন্টারেক্টিভভাবে যা পছন্দ করুন তা করুন তবে কোনও স্ক্রিপ্ট ফাইলে এবং পরামর্শ দেওয়ার সময় এটি সঠিকভাবে করুন।
Sorpigal

3

আপনি এর জন্য জিএনইউ অনুসন্ধানটি ব্যবহার করতে পারেন:

find -maxdepth 1 -type f -printf '%T@ %p\n' \
  | sort -k1,1 -g | head -20 | sed 's/^[0-9.]\+ //' \
  | xargs echo mv -t dest_dir

যেখানে পরিবর্তনের সময় (1970 থেকে সেকেন্ডে) এবং বর্তমান ডিরেক্টরিটির প্রতিটি ফাইলের নাম মুদ্রণ করে সেখানে আউটপুট প্রথম ক্ষেত্র অনুসারে সাজানো হয়, 20 প্রাচীনতম ফিল্টার করে এবং সেখানে স্থানান্তরিত হয় dest_direchoআপনি যদি কমান্ড লাইনটি পরীক্ষা করে থাকেন তবে এটি সরান ।


2

কেউই (এখনও) বাশ উদাহরণ পোস্ট করেনি যা ফাইলনামে এম্বেড করা নিউলাইন চরগুলি (এম্বেড করা কিছু) সরবরাহ করে, তাই এখানে একটি। এটি 3 প্রাচীনতম (এমডেট) নিয়মিত ফাইলগুলিকে সরিয়ে দেয়

move=3
find . -maxdepth 1 -type f -name '*' \
 -printf "%T@\t%p\0" |sort -znk1 | { 
  while IFS= read -d $'\0' -r file; do
      printf "%s\0" "${file#*$'\t'}"
      ((--move==0)) && break
  done } |xargs -0 mv -t dest

এটি টেস্ট-ডেটা স্নিপেট

# make test files with names containing \n, \t and "  "
rm -f '('?[1-4]'  |?)'
for f in $'(\n'{1..4}$'  |\t)' ;do sleep .1; echo >"$f" ;done
touch -d "1970-01-01" $'(\n4  |\t)'
ls -ltr '('?[1-4]'  |'?')'; echo
mkdir -p dest

এখানে চেক ফলাফল স্নিপেট

  ls -ltr '('?[1-4]'  |'?')'
  ls -ltr   dest/*

+1, আমার আগে কার্যকর দরকারী উত্তর (এবং এটি পরীক্ষার ডেটা পাওয়া সর্বদা ভাল))
সর্পিগাল

0

জিএনইউ দিয়ে করা সবচেয়ে সহজ find। এক দিনেরও বেশি পুরানো আমার ভিডিও নজরদারি সিস্টেম থেকে রেকর্ডিংগুলি মুছতে আমি এটি প্রতিদিন আমার লিনাক্স ডিভিআর-তে ব্যবহার করি।

এখানে বাক্য গঠন:

find /path/to/files/* -mtime +number_of_days -exec mv {} /path/to/folder \;

মনে রাখবেন findমৃত্যুদন্ড কার্যকর হওয়ার সময় থেকে 24 ঘন্টা হিসাবে কোনও দিনটিকে সংজ্ঞায়িত করা হয়। অতএব রাত ১১ টায় সর্বশেষ সংশোধিত ফাইলগুলি সকাল 1 টায় মুছবে না।

এমনকি আপনি একত্রিত করতে পারেন find সঙ্গে cron, তাই মুছে দেওয়া root পরিচয়ে নিম্নলিখিত কমান্ড চলমান দ্বারা স্বয়ংক্রিয়ভাবে নির্ধারিত করা যেতে পারে:

crontab -e << EOF
@daily /usr/bin/find /path/to/files/* -mtime +number_of_days -exec mv {} /path/to/folder \;
EOF

আপনি সর্বদা findএটির ম্যানুয়াল পৃষ্ঠার পরামর্শ নিয়ে আরও তথ্য পেতে পারেন :

man find

0

অন্যান্য উত্তরগুলি আমার এবং প্রশ্নের উদ্দেশ্যে ফিট করে না, এই শেলটি সেন্টোস 7 এ পরীক্ষা করা হয়:

oldestDir=$(find /yourPath/* -maxdepth 0 -type d -printf '%T+ %p\n' | sort | head -n 1 | tr -s ' ' | cut -d ' ' -f 2)
echo "$oldestDir"
rm -rf "$oldestDir"
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.