Làm cách nào tôi có thể sắp xếp các tệp dựa trên tên tệp đầu tiên của chúng vào các thư mục AZ


15

Tôi đang tìm cách (tốt nhất là thiết bị đầu cuối) để tổ chức hơn 1000 phông chữ bằng chữ cái đầu tiên của họ.

Về cơ bản tạo thư mục A-Z, #sau đó di chuyển các tệp phông chữ đến các thư mục đó dựa trên tên tệp đầu tiên của họ. Các phông chữ bắt đầu bằng số [0-9] hoặc các ký tự đặc biệt khác sẽ được chuyển đến #thư mục.


Bạn có muốn các thư mục được thực hiện ngay cả khi không có tệp nào bắt đầu bằng chữ cái đó không?
Arronical

@Arronical Nope. Chỉ cần có tập tin.
Parto

4
Tôi hy vọng liên kết này sẽ giúp bạn stackoverflow.com/questions/1251938/ từ
Karthickeyan

Câu trả lời:


13

Một tùy chọn trăn muộn:

#!/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))

Cách sử dụng

  1. Sao chép tập lệnh vào một tập tin trống, lưu nó dưới dạng move_files.py
  2. Chạy nó với thư mục là đối số:

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

Kịch bản sẽ chỉ tạo thư mục (phụ) (-ies) (chữ hoa) nếu thực sự cần thiết

Giải trình

Kịch bản:

  • liệt kê các tệp, nhận ký tự đầu tiên (xác định đường dẫn nguồn):

    for f in os.listdir(dr):
        s = f[0]; fsrc = path(dr, f)
  • kiểm tra nếu mục đó là một tập tin:

    if os.path.isfile(fsrc):
  • định nghĩa thư mục được nhắm mục tiêu cho nếu char đầu tiên là alpha hay không:

    target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
  • kiểm tra xem thư mục đã tồn tại hay chưa, tạo nó nếu không:

    if not os.path.exists(target):
        os.mkdir(target)
  • di chuyển mục vào thư mục tương ứng của nó:

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

Này Jacob. Có kiểm tra chữ hoa đầu tiên không?
Parto

@Parto Tuyệt đối! Bạn cần nó theo cách nào? (Tôi có thể kiểm tra trong 3,5 giờ giảng dạy :)
Jacob Vlijm

Nó thực sự đã làm. Thực hiện hoàn hảo.
Parto

Sau khi xem xét, tôi đã quyết định đi với câu trả lời này vì nhiều lý do: 1). Giải thích về những gì đang xảy ra. 2). Câu trả lời chạy chính xác trong lần thử đầu tiên
Parto

11

Code-golfed có thể đọc được chỉ với hai lệnh và hai biểu thức chính quy:

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

Nếu bạn có một lượng lớn tệp cần di chuyển, quá nhiều để phù hợp với danh sách đối số quy trình (vâng, có giới hạn và có thể chỉ là vài kilobyte), bạn có thể tạo danh sách tệp bằng một lệnh và đường ống khác để prename, ví dụ:

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

Điều này có thêm lợi ích của việc không cố gắng di chuyển tên tệp bằng chữ [[:alnum:]]?*nếu không có tệp nào khớp với mẫu toàn cục. findcũng cho phép nhiều tiêu chí phù hợp hơn so với shellbing. Một cách khác là đặt nullglobtùy chọn shell và đóng luồng đầu vào tiêu chuẩn của prename. 1

Trong cả hai trường hợp, loại bỏ công -ntắc để thực sự di chuyển các tệp và không chỉ hiển thị cách chúng sẽ được di chuyển.

Phụ lục: Bạn có thể xóa các thư mục trống một lần nữa với:

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

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


8

Nếu bạn không nhớ zsh, một hàm và một vài zmvlệnh:

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'

Các mmvchức năng làm cho các thư mục và di chuyển các tập tin. zmvsau đó cung cấp mô hình phù hợp và thay thế. Đầu tiên, di chuyển tên tệp bắt đầu bằng một bảng chữ cái, sau đó mọi thứ khác:

$ 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

Chạy lại mà không có echotrong mmv's định nghĩa để thực sự thực hiện các di chuyển.


8

Tôi đã không tìm ra một cách hay để đặt tên thư mục thành chữ hoa (hoặc di chuyển các tệp bằng chữ in hoa), mặc dù sau đó bạn có thể làm điều đó với 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

hoặc dễ đọc hơn:

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

Xóa echosau khi kiểm tra để thực sự di chuyển các tập tin

Và sau đó

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

loại bỏ -nnếu nó trông tốt sau khi thử nghiệm và chạy lại.


2
Bạn có thể sử dụng if [[ -d "${i^}" ]]để tạo ivốn thay đổi , và mkdir {A..Z}khi bắt đầu.
Arronical

Hãy để tôi thử cái này và xem
Parto

@Arronical cảm ơn! Tuy nhiên, tôi sẽ để nó, vì bạn đã đăng nó theo cách của riêng bạn
Zanna

@Zanna Tôi thích rằng chúng tôi đã đến nó từ các hướng khác nhau, lặp qua các chữ cái và sử dụng chúng làm tiêu chí tìm kiếm chưa bao giờ xảy ra với tôi. Tôi chắc chắn có một giải pháp thông minh và nhanh chóng với tìm kiếm, nhưng không thể hiểu được!
Arronical

Này Zanna, điều này đã không di chuyển các phông chữ bắt đầu bằng một chữ cái viết hoa. Nếu không thì làm việc tốt.
Parto

7

Các lệnh sau trong thư mục chứa phông chữ sẽ hoạt động, nếu bạn muốn sử dụng từ bên ngoài thư mục lưu trữ phông chữ, hãy đổi for f in ./*thành for f in /directory/containing/fonts/*. Đây là một phương pháp dựa trên shell, nên khá chậm, và cũng không đệ quy. Điều này sẽ chỉ tạo thư mục, nếu có các tệp bắt đầu bằng ký tự trùng khớp.

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

Là một lớp lót, một lần nữa từ trong thư mục lưu trữ phông chữ:

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

Một phương thức sử dụng find, với thao tác chuỗi tương tự, sử dụng mở rộng tham số bash, sẽ được đệ quy và sẽ nhanh hơn một chút so với phiên bản shell thuần:

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' \;

Hoặc dễ đọc hơn:

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' \;

Cái này làm việc quá. Ngay cả đối với chữ hoa và chữ thường.
Parto

5

Ánh xạ mỗi tên tệp vào một tên thư mục bằng cách sử dụng tr, sau đó mkdirmv:

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'

Tôi thực sự thích điều này, đây là những gì tôi đã nắm bắt được với mong muốn sử dụng find. Có cách nào để chỉ tạo tên thư mục chữ hoa và di chuyển tên tệp chữ thường vào chúng không?
Arronical

1
Tôi đã thêm một cái khác trđể chuyển đổi thành chữ hoa.
xn.

Tại sao đường vòng qua xargschỉ để gọi bashlại? Sẽ không đơn giản và dễ đọc hơn nhiều khi chuyển đầu ra của findmột vòng lặp while và readnó có ghi theo từng bản ghi không?
David foerster

Câu hỏi hay, @DavidFoerster. Tôi đoán sự thiên vị chống lại các vòng lặp trong một lớp lót và ưu tiên cho một cách tiếp cận chức năng hơn. Nhưng một tập lệnh bash trong một chuỗi cũng không phải là rất thanh lịch, vì vậy tôi nói rằng whilephiên bản vòng lặp ( bit.ly/2j2mhyb ) có lẽ tốt hơn.
xn.

Nhân tiện, bạn có thể tránh sự {}thay thế khó chịu nếu bạn cho phép xargsnối thêm đối số và sau đó tham khảo $1bên trong tập lệnh shell, ví dụ : xargs -0 -n1 -- bash -c 'dir=/dest/$(basename "$1" | ...); ...; mv "$1" "$dir"' _. (Hãy nhớ trận chung kết _!)
David Foerster
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.