প্রতি 12 তম ফাইল বাদে সমস্ত সরান


14

আমার কাছে ফাইল নাম নাম্বার 2323.end এ কয়েক হাজার ফাইল রয়েছে। আমি কেবল প্রতি 12 তম ফাইল রাখতে চাই, তাই ফাইল 1000.000.end, file.00024.end ... file.99996.end করুন এবং সমস্ত কিছু মুছুন।

ফাইলগুলির নামের আগে ফাইলগুলির সংখ্যাও থাকতে পারে এবং এটি সাধারণত ফর্মের হয়: file.00064.name.99999.end

আমি ব্যাশ শেল ব্যবহার করি এবং ফাইলগুলি কীভাবে লুপ করবেন তা বের করতে পারি না এবং তারপরে নম্বরটি বের করে তা পরীক্ষা না করে এটি number%%12=0 ফাইলটি মুছে ফেলা হচ্ছে কিনা । আমাকে কি কেউ সাহায্য করতে পারবেন?

ধন্যবাদ, ডোরিনা


ফাইলের নাম্বার কি কেবল ফাইলের উপর নির্ভরশীল?
অ্যারোনিকাল

এছাড়াও, ফাইলগুলিতে সর্বদা 5 টি সংখ্যা থাকে এবং প্রত্যয় এবং উপসর্গটি কি সর্বদা একই থাকে?
অ্যারোনিকাল

হ্যাঁ এটি সর্বদা 5 ডিজিটের হয়। আমি আপনার প্রথম প্রশ্নটি সঠিকভাবে পেয়েছি কিনা তা নিশ্চিত নই। বিভিন্ন ফাইলের নামের সাথে ফাইল ভিন্ন, এবং আমি যা 00012, 00024 ইত্যাদি নম্বর আছে ঘটতে এই নির্দিষ্ট ফাইল প্রয়োজন
Dorina

3
@ ডরিনা দয়া করে আপনার প্রশ্নটি সম্পাদনা করুন এবং এটি পরিষ্কার করুন। এটা সব পরিবর্তন!
টেরডন

2
এবং তারা সব একই ডিরেক্টরিতে আছেন, তাই না?
সের্গেই কোলোডিয়াজনি

উত্তর:


18

এখানে একটি পার্ল সমাধান। হাজার হাজার ফাইলের জন্য এটি আরও দ্রুত হওয়া উচিত:

perl -e '@bad=grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV; unlink @bad' *

এটিকে আরও ঘনীভূত করা যেতে পারে:

perl -e 'unlink grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV;' *

আপনার যদি খুব বেশি ফাইল থাকে এবং সাধারণটি ব্যবহার করতে না পারেন তবে *আপনি এমন কিছু করতে পারেন:

perl -e 'opendir($d,"."); unlink grep{/(\d+)\.end/ && $1 % 12 != 0} readdir($dir)'

গতি হিসাবে, এখানে এই পদ্ধতির একটি তুলনা এবং অন্য একটির উত্তরে শেলটি দেওয়া হয়েছে:

$ touch file.{01..64}.name.{00001..01000}.end
$ ls | wc
  64000   64000 1472000
$ time for f in ./* ; do file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; done

real    2m44.258s
user    0m9.183s
sys     1m7.647s

$ touch file.{01..64}.name.{00001..01000}.end
$ time perl -e 'unlink grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV;' *

real    0m0.610s
user    0m0.317s
sys     0m0.290s

আপনি দেখতে পাচ্ছেন, পার্থক্যটি বিশাল, প্রত্যাশার মতো

ব্যাখ্যা

  • -eকেবল বলছে perlস্ক্রিপ্ট কমান্ড লাইন দেওয়া চালানোর জন্য।
  • @ARGVস্ক্রিপ্টে দেওয়া সমস্ত আর্গুমেন্ট সম্বলিত একটি বিশেষ পরিবর্তনশীল। যেহেতু আমরা এটি দিচ্ছি, এতে *বর্তমান ডিরেক্টরিতে থাকা সমস্ত ফাইল (এবং ডিরেক্টরিগুলি) থাকবে।
  • grepফাইলের নাম তালিকা গবেষণা ও কোনো সংখ্যার একটি স্ট্রিং, একটি বিন্দু এবং মেলে জন্য চেহারা হবে end( /(\d+)\.end/)

  • যেহেতু সংখ্যাগুলি ( \d) একটি ক্যাপচার গ্রুপে (প্রথম বন্ধনী) রয়েছে, সেগুলি সেভ করে $1। সুতরাং grepউইলটি তখন পরীক্ষা করবে যে এই সংখ্যাটি 12 এর একাধিক কিনা এবং যদি তা না হয় তবে ফাইলটির নাম ফিরে আসবে। অন্য কথায়, অ্যারে @badমুছে ফেলার জন্য ফাইলগুলির তালিকা ধারণ করে।

  • এরপরে তালিকাটি পাস করা হয় unlink()যা ফাইলগুলি সরিয়ে দেয় (তবে ডিরেক্টরিগুলি নয়)।


12

আপনার ফাইলের নামগুলি ফর্ম্যাটে রয়েছে তা প্রদত্ত file.00064.name.99999.end, আমাদের প্রথমে আমাদের সংখ্যাটি বাদ দিয়ে সবকিছু ছাঁটাই করতে হবে। এটি করার জন্য আমরা একটি forলুপ ব্যবহার করব ।

বেস 10 ব্যবহার করার জন্য আমাদের বাশ শেলকেও বলা দরকার, কারণ বাশ পাটিগণিত তাদের 0 টি বেস 8 হিসাবে শুরু করে সংখ্যার সাথে আচরণ করবে, যা আমাদের জন্য জিনিসগুলিকে গোলমাল করবে।

স্ক্রিপ্ট হিসাবে, ফাইল যুক্ত ডিরেক্টরিতে যখন ব্যবহার করা হয়:

#!/bin/bash

for f in ./*
do
  if [[ -f "$f" ]]; then
    file="${f%.*}"
    if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then
      rm "$f"
    fi
  else
    echo "$f is not a file, skipping."
  fi
done

অথবা আপনি একই কাজটি করতে এই দীর্ঘ কুরুচিপূর্ণ আদেশটি ব্যবহার করতে পারেন:

for f in ./* ; do if [[ -f "$f" ]]; then file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; else echo "$f is not a file, skipping."; fi; done

সমস্ত অংশ ব্যাখ্যা করার জন্য:

  • for f in ./* বর্তমান ডিরেক্টরিতে প্রতিটি কিছুর জন্য, করুন .... এটি প্রতিটি ফাইল বা ডিরেক্টরিটি ভেরিয়েবল $ এফ হিসাবে সন্ধান করে।
  • if [[ -f "$f" ]]খুঁজে পাওয়া আইটেমটি একটি ফাইল কিনা তা যাচাই করে আমরা যদি সেই echo "$f is not...অংশে না যাই , যার অর্থ আমরা দুর্ঘটনাক্রমে ডিরেক্টরিগুলি মুছতে শুরু করি না।
  • file="${f%.*}" ফাইলের নাম হিসাবে শেষের পরে যা আসে তা ছাঁটাই করে ফাইল ফাইল হিসাবে পরিবর্তনশীল সেট করে .
  • if [[ $((10#${file##*.} % 12)) -eq 0 ]]যেখানে হবে। প্রধান পাটিগণিত কিক ${file##*.}গত এর আগে সবকিছু ঠিকঠাক ট্রিম .এক্সটেনশন ছাড়া আমাদের ফাইলের নাম হবে। $(( $num % $num2 ))মডুলো অপারেশনটি ব্যবহার করার জন্য বাশ পাটিগণিতের বাক্য গঠন, 10#এটি শুরুতে বাশকে বেস 10 ব্যবহার করতে বলে, যেসব সমস্যাযুক্ত নেতৃস্থানীয় 0s ব্যবহার করে। $((10#${file##*.} % 12))তারপরে আমাদের 12 -ne 0টির সাথে ভাগ করে নেওয়া আমাদের ফাইলনাম সংখ্যা বাকী রেখে দেয় leaves বাকীটি শূন্যের সাথে "সমান নয়" কিনা তা পরীক্ষা করে।
  • যদি বাকীটি 0 এর সমান না হয়, ফাইলটি rmকমান্ডের সাথে মুছে ফেলা হয় , আপনি প্রথমে এটি চালনার rmসাথে সাথে প্রতিস্থাপন করতে চাইতে echoপারেন, প্রত্যাশিত ফাইলগুলি মুছে ফেলা হবে কিনা তা পরীক্ষা করতে।

এই সমাধানটি পুনরাবৃত্তিমূলক, এর অর্থ এটি কেবল বর্তমান ডিরেক্টরিতে ফাইলগুলি প্রসেস করবে, এটি কোনও উপ ডিরেক্টরিতে যাবে না।

ifসঙ্গে বিবৃতি echoকমান্ড ডিরেক্টরি সম্পর্কে সতর্ক সত্যিই প্রয়োজনীয় হিসাবে নয় rmতে এটি নিজের ব্যাপার ডিরেক্টরি সম্পর্কে অভিযোগ করা হবে, এবং না তাদের মুছে, তাই:

#!/bin/bash

for f in ./*
do
  file="${f%.*}"
  if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then
    rm "$f"
  fi
done

অথবা

for f in ./* ; do file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; done

সঠিকভাবে কাজ করবে।


5
rmকয়েক হাজার বার কল করা বেশ ধীর হতে পারে। আমি সুপারিশ echoপরিবর্তে ফাইল নাম এবং পাইপ থেকে লুপ আউটপুট xargs rm(অ্যাড অপশন হিসাবে প্রয়োজন): for f in *; do if ... ; then echo "$f"; fi; done | xargs -rd '\n' -- rm --
ডেভিড ফোস্টার

আমি আপনার প্রস্তাবিত গতি উন্নতি অন্তর্ভুক্ত সম্পাদনা করেছি।
অ্যারোনিকাল

প্রকৃতপক্ষে 55999 ফাইল সহ ডিরেক্টরিতে পরীক্ষা করার পরে, আসল সংস্করণটি xargs2 মিনিট 48 সেকস, সংস্করণে 5 মিনিট 1 সেকেন্ড নিয়েছিল এটি কি echoডেভিডফোস্টারসে ওভারহেডের কারণে হতে পারে ?
অ্যারোনিকাল

অস্বাভাবিক. 60.000 ফাইলের জন্য আমি একটি tmpfs time { for f in *; do echo "$f"; done | xargs rm; }সহ 1m11.450s / 0m10.695s / 0m16.800s বনাম 0m0.659s / 0m0.545s / 0m0.380s (আসল / ব্যবহারকারী / sys) পাই time { for f in *; do rm "$f"; done; }। বাশটি v4.3.11, কার্নেলটি v4.4.19।
ডেভিড ফোস্টার

6

আপনি প্রতি 12 তম সংখ্যাযুক্ত নাম উত্পন্ন করতে বাশ বন্ধনী সম্প্রসারণ ব্যবহার করতে পারেন। আসুন কিছু পরীক্ষার ডেটা তৈরি করি

$ touch file.{0..9}{0..9}{0..9}{0..9}{0..9}.end # create test data
$ mv file.00024.end file.00024.end.name.99999.end # testing this form of filenames

তারপরে আমরা নিম্নলিখিতটি ব্যবহার করতে পারি

$ ls 'file.'{00012..100..12}* # print these with numbers less than 100
file.00012.end                 file.00036.end  file.00060.end  file.00084.end
file.00024.end.name.99999.end  file.00048.end  file.00072.end  file.00096.end
$ rm 'file.'{00012..100000..12}* # do the job

যদিও প্রচুর পরিমাণে ফাইলের জন্য আশাহীনভাবে ধীর কাজ করে - হাজার হাজার নাম তৈরি করতে সময় এবং মেমরি লাগে so সুতরাং এটি আরও কার্যকর যে আসল দক্ষ সমাধান।


আমি এই এক কোড গল্ফ পছন্দ।
ডেভিড ফোস্টার

1

একটু লম্বা, তবে আমার মনে এটাই আসে।

 for num in $(seq 1 1 11) ; do
     for sequence in $(seq -f %05g $num 12 99999) ; do
         rm file.$sequence.end.99999;
     done
 done

ব্যাখ্যা: প্রতি 12 তম ফাইলটি এগার বার মুছুন।


0

সমস্ত নম্রতার মধ্যে আমি মনে করি এই সমাধানটি অন্য উত্তরের চেয়ে অনেক সুন্দর ic

find . -name '*.end' -depth 1 | awk 'NR%12 != 0 {print}' | xargs -n100 rm

একটু ব্যাখ্যা: প্রথমে আমরা ফাইলগুলির একটি তালিকা তৈরি করি find। আমরা সমস্ত ফাইল পেয়েছি যার নামটি শেষ হয় .endএবং যা 1 গভীরতার সাথে থাকে (এটি বলতে গেলে তারা সরাসরি ওয়ার্কিং ডিরেক্টরিতে থাকে এবং কোনও সাবফোল্ডারগুলিতে নয় sub সাবফোল্ডার না থাকলে আপনি এটি ছেড়ে দিতে পারেন)। আউটপুট তালিকা বর্ণমালা অনুসারে বাছাই করা হবে।

তারপরে আমরা সেই তালিকাটি পাইপ করি awk, যেখানে আমরা বিশেষ ভেরিয়েবলটি ব্যবহার করি NRযা লাইন নম্বর। আমরা প্রতি 12 তম ফাইলটি যেখানে ফাইলগুলি মুদ্রণ করে রেখে যাই NR%12 != 0awkকমান্ড সংক্ষিপ্ত করা যেতে পারে awk 'NR%12', কারণ ফরম অপারেটরের ফলাফল একটি বুলিয়ান মান হিসেবে ব্যাখ্যা পরার এবং {print}পরোক্ষভাবে যাহাই হউক না কেন সম্পন্ন করা হয়।

সুতরাং এখন আমাদের ফাইলগুলির একটি তালিকা রয়েছে যা মুছতে হবে, যা আমরা xargs এবং rm দিয়ে করতে পারি। xargsপ্রদত্ত কমান্ড ( rm) আর্গুমেন্ট হিসাবে স্ট্যান্ডার্ড ইনপুট দিয়ে চালায় ।

আপনার যদি অনেকগুলি ফাইল থাকে তবে আপনি 'যুক্তি তালিকা খুব দীর্ঘ' এর মতো কিছু বলতে ত্রুটি পাবেন (আমার মেশিনে যে সীমাটি 256 কেবি, এবং নূন্যতম পসিক্সের প্রয়োজন 4096 বাইট)। এটি -n 100পতাকা দ্বারা এড়ানো যায় যা প্রতি 100 টি শব্দের মধ্যে যুক্তিগুলি বিভক্ত করে (লাইন নয়, আপনার ফাইলের নামের ফাঁকা থাকে কিনা তা দেখার জন্য কিছু) এবং rmকেবলমাত্র 100 টি আর্গুমেন্টের সাথে একটি পৃথক কমান্ড কার্যকর করে।


3
আপনার পদ্ধতির সাথে বেশ কয়েকটি সমস্যা রয়েছে: -depthআগে হওয়া দরকার -name; ii) যদি ফাইলের নামের কোনওটিতে সাদা স্থান থাকে তবে এটি ব্যর্থ হবে; iii) আপনি ধরে নিচ্ছেন যে ফাইলগুলি আরোহী সংখ্যায় ক্রমে তালিকাভুক্ত হবে ( awkএটিই আপনার পরীক্ষার জন্য যা করছেন) তবে এটি অবশ্যই ঘটবে না। সুতরাং, এটি ফাইলের একটি এলোমেলো সেট মুছবে।
টেরডন

d'আহা! আপনি বেশ সঠিক, আমার খারাপ (মন্তব্য সম্পাদিত)। ভুল স্থান নির্ধারণের কারণে আমি ত্রুটি পেয়েছি এবং মনে নেই -depth। তবুও, এটি এখানে সমস্যাগুলির মধ্যে সর্বনিম্ন ছিল, সর্বাধিক গুরুত্বপূর্ণটি হ'ল আপনি ফাইলের একটি এলোমেলো সংকলন মুছে ফেলছেন এবং ওপি চাইছেন না not
টেরডন

ওহ, এবং না, -depthকোনও মূল্য নেয় না এবং এটি আপনার যা ভাবেন তার বিপরীত কাজ করে। দেখুন man find: "-ডাইরেক্টরি নিজেই ডিরেক্টরি আগে প্রতিটি ডিরেক্টরি এর বিষয়বস্তু প্রসেস।" সুতরাং এটি আসলে উপ-ডিরেক্টরিতে নেমে আসবে এবং পুরো জায়গা জুড়ে বিপর্যয় ডেকে আনবে।
টেরডন

আমি) উভয় -depth nএবং -maxdepth nবিদ্যমান। পূর্বেরটির গভীরতা হ'ল n হওয়া দরকার এবং পরেরটির সাথে এটি <= n হতে পারে। দ্বিতীয়)। হ্যাঁ, এটি খারাপ তবে এই বিশেষ উদাহরণের জন্য এটি কোনও উদ্বেগ নয়। আপনি এটি ব্যবহার করে এটি ঠিক করতে পারেন find ... -print0 | awk 'BEGIN {RS="\0"}; NR%12 != 0' | xargs -0 -n100 rmযা রেকর্ড বিভাজক হিসাবে নাল বাইট ব্যবহার করে (যা ফাইলের নামগুলিতে অনুমোদিত নয়)। তৃতীয়) আবারও, এক্ষেত্রে অনুমান যুক্তিসঙ্গত। অন্যথায় আপনি একটি এর sort -nমধ্যে findএবং সন্নিবেশ করিয়ে দিতে পারেন awk, বা findকোনও ফাইলে পুনর্নির্দেশ করতে পারেন এবং আপনার পছন্দমতো বাছাই করতে পারেন।
ব্যবহারকারী593851

3
আহ, আপনি সম্ভবত তখন ওএসএক্স ব্যবহার করছেন। এটি একটি খুব আলাদা বাস্তবায়ন find। আবার, তবে, প্রধান সমস্যাটি আপনি ধরে নিচ্ছেন যে এটি findএকটি সাজানো তালিকাটি দেয়। এটা হয় না।
টেরডন

0

কেবল বাশ ব্যবহারের জন্য, আমার প্রথম পদ্ধতির প্রতিযোগিতাটি হ'ল: ১. আপনি যে সমস্ত ফাইল রাখতে চান তা অন্য ডিরেক্টরিতে নিয়ে যান (উদাহরণস্বরূপ, যাদের ফাইলের নাম সংখ্যা 12 এর একাধিক) তারপর ২. ডিরেক্টরিতে থাকা সমস্ত ফাইল মুছুন, তারপরে 3. 12 টি একাধিক ফাইল আপনি যেখানে রেখেছিলেন সেখানে রেখে দিন। সুতরাং এর মতো কিছু কাজ করতে পারে:

cd dir_containing_files
mkdir keep_these_files
n=0
while [ "${n}" -lt 99999 ]; do
  padded_n="`echo -n "00000${n}" | tail -c 5`"
  mv "filename${padded_n}.end" keep_these_files/
  n=$[n+12]
done
rm filename*.end
mv keep_these_files/* .
rmdir keep_these_files

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