मैं AZ फ़ोल्डर में उनके फ़ाइल नाम के पहले अक्षर के आधार पर फ़ाइलों को कैसे व्यवस्थित कर सकता हूं


15

मैं अपने पहले पत्र द्वारा 1000 से अधिक फोंट आयोजित करने के लिए एक रास्ता (अधिमानतः टर्मिनल) की तलाश कर रहा हूं।

मूल रूप से डायरेक्टरी बनाते हैं A-Z, #फिर फॉन्ट फाइल्स को उन फाइल्स पर ले जाते हैं जो उनके फ़ाइल नाम के पहले कैरेक्टर पर आधारित होती हैं। फ़ॉन्ट [0-9] या अन्य विशेष वर्णों से शुरू होने वाले फ़ॉन्ट को #निर्देशिका में ले जाया जाता है ।


क्या आप चाहते हैं कि उस पत्र के साथ शुरू होने वाली फाइलें न होने पर भी निर्देशिका बनाई जाए?
एरोनिकल

@ एरॉनिकल नोप। बस अगर फाइलें हैं।
पार्टो

4
मुझे आशा है कि यह लिंक आपको stackoverflow.com/questions/1251938/…
Karthickeyan

जवाबों:


13

एक दिवंगत अजगर विकल्प:

#!/usr/bin/env python3
import os
import sys
import shutil

def path(dr, f): return os.path.join(dr, f)

dr = sys.argv[1]
for f in os.listdir(dr):
    fsrc = path(dr, f)
    if os.path.isfile(fsrc):
        s = f[0]; target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
        if not os.path.exists(target):
            os.mkdir(target)
        shutil.move(fsrc, path(target, f))

कैसे इस्तेमाल करे

  1. स्क्रिप्ट को एक खाली फ़ाइल में कॉपी करें, इसे इस रूप में सहेजें move_files.py
  2. तर्क के रूप में इसे निर्देशिका के साथ चलाएँ:

    python3 /path/to/move_files.py /path/to/files
    

स्क्रिप्ट केवल (उप) निर्देशिका (-ies) (अपरकेस) बनाएगी यदि यह वास्तव में आवश्यक है

व्याख्या

लिपी:

  • फ़ाइलों को सूचीबद्ध करता है, पहला वर्ण प्राप्त करता है (सोर्सपाथ को परिभाषित करता है):

    for f in os.listdir(dr):
        s = f[0]; fsrc = path(dr, f)
  • जाँचता है कि क्या आइटम एक फ़ाइल है:

    if os.path.isfile(fsrc):
  • यदि पहले चार अक्षर अल्फा है या नहीं, तो लक्षित फ़ोल्डर को परिभाषित करता है:

    target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
  • जाँच करता है कि फ़ोल्डर पहले से मौजूद है या नहीं, यदि नहीं तो इसे बनाता है:

    if not os.path.exists(target):
        os.mkdir(target)
  • आइटम को उसके संबंधित फ़ोल्डर में ले जाता है:

    shutil.move(fsrc, path(target, f))

हे जैकब। क्या पहले अक्षरों के लिए बड़ा चेक है?
भागो

@Parto बिल्कुल! किस तरह से आपको इसकी आवश्यकता है? (मैं 3,5 बजे शिक्षण में जाँच कर सकता हूँ :)
जैकब व्लिजम

यह वास्तव में किया था। सही कार्यान्वयन।
13

विचार करने के बाद, मैंने कई कारणों से इस उत्तर के साथ जाने का फैसला किया है: 1)। क्या चल रहा है, इस पर स्पष्टीकरण। 2)। पहली कोशिश में जवाब सही तरीके से चलता है
पार्टो

11

कोड-गोल्फ अभी तक केवल दो आदेशों और दो नियमित अभिव्यक्तियों के साथ पठनीय है:

mkdir -p '#' {a..z}
prename -n 's|^[[:alpha:]]|\l$&/$&|; s|^[0-9]|#/$&|' [[:alnum:]]?*

यदि आपके पास स्थानांतरित करने के लिए बड़ी मात्रा में फाइलें हैं, तो बहुत से प्रक्रिया तर्क सूची में फिट होने के लिए (हां, इसकी एक सीमा है और यह सिर्फ कुछ किलोबाइट हो सकता है), आप एक अलग कमांड और पाइप के साथ फाइल सूची तैयार कर सकते हैं prename, जैसे:

find -mindepth 1 -maxdepth 1 -name '[[:alnum:]]?*' -printf '%f\n' |
prename -n 's|^[[:alpha:]]|\l$&/$&|; s|^[0-9]|#/$&|'

[[:alnum:]]?*यदि फ़ाइल कोई ग्लोब पैटर्न से मेल नहीं खाती है, तो शाब्दिक फ़ाइल नाम को स्थानांतरित करने का प्रयास न करने का अतिरिक्त लाभ है । findशेल गोलाबारी की तुलना में कई और अधिक मैच मापदंड की अनुमति देता है। वैकल्पिक विकल्प nullglobशेल विकल्प सेट करना और मानक इनपुट स्ट्रीम को बंद करना है prename1

दोनों मामलों में -nवास्तव में फ़ाइलों को स्थानांतरित करने के लिए स्विच को हटा दें और न केवल यह दिखाएं कि उन्हें कैसे स्थानांतरित किया जाएगा।

परिशिष्ट: आप खाली निर्देशिकाओं को फिर से निकाल सकते हैं:

rmdir --ignore-fail-on-non-empty '#' {a..z}

1 shopt -s nullglob; prename ... <&-


8

यदि आप zsh को बुरा नहीं मानते, तो एक फंक्शन और कुछ zmvकमांड्स:

mmv() {echo mkdir -p "${2%/*}/"; echo mv -- "$1" "$2";}
autoload -U zmv
zmv -P mmv '([a-zA-Z])(*.ttf)' '${(UC)1}/$1$2'
zmv -P mmv '([!a-zA-Z])(*.ttf)' '#/$1$2'

mmvसमारोह निर्देशिका बनाता है और फ़ाइल ले जाता है। zmvफिर पैटर्न-मिलान और प्रतिस्थापन प्रदान करता है। सबसे पहले, एक वर्णमाला से शुरू होने वाले फ़ाइल नाम, फिर बाकी सब कुछ:

$ zmv -P mmv '([a-zA-Z])(*.ttf)' '${(UC)1}/$1$2'
mkdir -p A/
mv -- abcd.ttf A/abcd.ttf
mkdir -p A/
mv -- ABCD.ttf A/ABCD.ttf
$ zmv -P mmv '([!a-zA-Z])(*.ttf)' '#/$1$2'
mkdir -p #/
mv -- 123.ttf #/123.ttf
mkdir -p #/
mv -- 七.ttf #/七.ttf

बिना फिर से चलाने echoमें mmvकी परिभाषा वास्तव में चाल प्रदर्शन करने के लिए।


8

मैं निर्देशिका नाम अपरकेस बनाने के लिए एक अच्छा तरीका काम नहीं किया (या अपरकेस अक्षरों के साथ फ़ाइलों को स्थानांतरित करने के लिए), हालांकि आप इसे बाद में कर सकते हैं rename...

mkdir {a..z} \#; for i in {a..z}; do for f in "$i"*; do if [[ -f "$f" ]]; then echo mv -v -- "$f" "$i"; fi; done; done; for g in [![:alpha:]]*; do if [[ -f "$g" ]]; then echo mv -v -- "$g" \#; fi; done

या अधिक आसानी से:

mkdir {a..z} \#; 
for i in {a..z}; do 
  for f in "$i"*; do
    if [[ -f "$f" ]]; then 
      echo mv -v -- "$f" "$i"; 
    fi 
  done
done
for g in [![:alpha:]]*; do 
  if [[ -f "$g" ]]; then 
    echo mv -v -- "$g" \#
  fi
done

echoवास्तव में फ़ाइलों को स्थानांतरित करने के लिए परीक्षण के बाद निकालें

और तब

rename -n 'y/[a-z]/[A-Z]/' *

निकालें -nअगर यह परीक्षण के बाद अच्छा लग रहा है और फिर से चला।


2
आप if [[ -d "${i^}" ]]परिवर्तनशील iपूंजी बनाने के लिए और mkdir {A..Z}शुरुआत में उपयोग कर सकते हैं ।
एरोनिकल

मुझे यह कोशिश करने दें और देखें
पार्टो

@Arronical धन्यवाद! हालाँकि, मैं इसे छोड़ दूंगा, क्योंकि आपने इसे अपने तरीके से पोस्ट किया था
Zanna

@ मुझे पसंद है कि हम इसे अलग-अलग दिशाओं से आए थे, पत्रों के माध्यम से पुनरावृत्ति करना और खोज मानदंडों के रूप में उनका उपयोग करना मेरे लिए कभी नहीं हुआ था। मुझे यकीन है कि खोजने के साथ एक चतुर और तेज़ समाधान है, लेकिन इसके चारों ओर मेरा सिर नहीं मिल सकता है!
एरोनिकल

हे Zanna, यह एक बड़े अक्षर के साथ शुरू होने वाले फोंट को स्थानांतरित नहीं किया। अन्यथा ठीक काम किया।
पार्टो

7

फोंट युक्त निर्देशिका के भीतर निम्नलिखित कमांड को काम करना चाहिए, यदि आप फ़ॉन्ट संग्रहण निर्देशिका के बाहर से उपयोग करना चाहते हैं, तो बदल for f in ./*दें for f in /directory/containing/fonts/*। यह एक बहुत ही शेल आधारित पद्धति है, इसलिए काफी धीमी है, और गैर-पुनरावर्ती भी है। यह केवल निर्देशिका बनाएगा, अगर ऐसी फाइलें हैं जो मिलान चरित्र से शुरू होती हैं।

target=/directory/to/store/alphabet/dirs
mkdir "$target"
for f in ./* ; do 
  if [[ -f "$f" ]]; then 
    i=${f##*/}
    i=${i:0:1}
    dir=${i^}
    if [[ $dir != [A-Z] ]]; then 
      mkdir -p "${target}/#" && mv "$f" "${target}/#"
    else
      mkdir -p "${target}/$dir" && mv "$f" "${target}/$dir"
    fi
  fi
done

एक लाइनर के रूप में, फिर से फ़ॉन्ट भंडारण निर्देशिका के भीतर से:

target=/directory/to/store/alphabet/dirs; mkdir "$target" && for f in ./* ; do if [[ -f "$f" ]]; then i=${f##*/}; i=${i:0:1} ; dir=${i^} ; if [[ $dir != [A-Z] ]]; then mkdir -p "${target}/#" && mv "$f" "${target}/#"; else mkdir -p "${target}/$dir" && mv "$f" "${target}/$dir" ; fi ; fi ; done

बैश पैरामीटर विस्तार का उपयोग करते हुए, समान स्ट्रिंग हेरफेर के साथ, खोजने का एक तरीका, जो पुनरावर्ती होगा, और शुद्ध शेल संस्करण की तुलना में कुछ तेज होना चाहिए:

find . -type f -exec bash -c 'target=/directory/to/store/alphabet/dirs ; mkdir -p "$target"; f="{}" ; i="${f##*/}"; i="${i:0:1}"; i=${i^}; if [[ $i = [[:alpha:]] ]]; then mkdir -p "${target}/$i" && mv "$f" "${target}/$i"; else mkdir -p "${target}/#" && mv "$f" "${target}/#"; fi' \;

या अधिक आसानी से:

find . -type f -exec bash -c 'target=/directory/to/store/alphabet/dirs 
   mkdir -p "$target"
   f="{}"
   i="${f##*/}"
   i="${i:0:1}"
   i=${i^}
   if [[ $i = [[:alpha:]] ]]; then 
      mkdir -p "${target}/$i" && mv "$f" "${target}/$i"
   else
      mkdir -p "${target}/#" && mv "$f" "${target}/#"
   fi' \;

इसने भी काम किया। यहां तक ​​कि अपरकेस और लोअरकेस अक्षरों के लिए भी।
पार्टो

5

प्रत्येक फ़ाइल नाम का उपयोग करके एक निर्देशिका नाम पर मैप करें tr, mkdirऔर फिर mv:

find /src/dir -type f -print0 |
xargs -0 -I{} bash -c \
  'dir=/dest/$(basename "{}" | cut -c1 | tr -C "a-zA-Z\n" "#" | tr "a-z "A-Z"); mkdir -p $dir; mv "{}" $dir'

मैं वास्तव में इसे पसंद करता हूं, यह उन पंक्तियों के साथ है जो मैं अपनी इच्छा से खोज के उपयोग के लिए समझ रहा था। वहाँ केवल ऊपरी मामले निर्देशिका नाम बनाने के लिए एक रास्ता है, और उन में निचले मामले फ़ाइल नाम स्थानांतरित?

1
मैंने trअपरकेस में बदलने के लिए एक और जोड़ा ।
xn।

xargsबस bashफिर से फोन करने के लिए चक्कर क्यों ? क्या यह एक सरल find-लूप के आउटपुट को पाइप करने के लिए सरल और बहुत अधिक पठनीय नहीं होगा और readयह रिकॉर्ड-दर-रिकॉर्ड है?
डेविड फ़ॉस्टर 20

अच्छा सवाल, @DavidFoerster। मैं एक-लाइनरों में छोरों के खिलाफ पूर्वाग्रह का अनुमान लगाता हूं और अधिक कार्यात्मक दृष्टिकोण के लिए प्राथमिकता देता हूं। लेकिन एक स्ट्रिंग में बैश स्क्रिप्ट बहुत सुरुचिपूर्ण नहीं है, इसलिए मैं कहूँगा कि whileपाश संस्करण ( bit.ly/2j2mhyb ) शायद बेहतर है।
xn।

वैसे, आप खराब {}प्रतिस्थापन से बच सकते हैं यदि आप xargsतर्क को जोड़ते हैं और फिर $1शेल स्क्रिप्ट के अंदर देखें , उदाहरण के लिए xargs -0 -n1 -- bash -c 'dir=/dest/$(basename "$1" | ...); ...; mv "$1" "$dir"' _:। (माइंड द फाइनल _!)
डेविड फ़ॉस्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.