如何根据文件名的第一个字母将文件组织到AZ文件夹中


15

我正在寻找一种方法(最好是终端)来按首字母组织1000多种字体。

基本上创建目录,A-Z, #然后根据文件名的第一个字符将字体文件移动到这些目录。以数字[0-9]或其他特殊字符开头的字体将被移动到#目录中。


即使没有以该字母开头的文件,您是否也要创建目录?
Arronical

@Arronical Nope。就算有文件。
Parto

4
我希望此链接可以帮助您stackoverflow.com/questions/1251938/…–
Karthickeyan

Answers:


13

较晚的python选项:

#!/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):
  • 定义第一个字符是否为alpha的目标文件夹:

    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

@Parto绝对!您以什么方式需要它?(我可以检查3,5小时的教学时间:)
Jacob Vlijm

确实做到了。完善的执行。
Parto'1

考虑之后,出于以下几个原因,我决定采用此答案:1)。关于发生了什么的解释。2)。答案在第一次尝试时正确运行
-Parto

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与shell锁定相比,还允许更多的匹配条件。一种替代方法是设置nullglobshell选项并关闭的标准输入流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

在没有echoin 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

让我尝试一下
-Parto

@Arronical谢谢!但是,由于您以自己的方式发布了它,我将保留它
Zanna

@Zanna我喜欢我们从不同的方向来看待它,反复遍历字母并将它们用作搜索条件对我而言从未发生过。我敢肯定,find会提供一个聪明而快速的解决方案,但我无法解决!
Arronical

嘿Zanna,这没有移动以大写字母开头的字体。否则效果很好。
Parto'1

7

包含字体的目录中的以下命令应该起作用,如果要从字体存储目录之外使用,请更改for f in ./*for f in /directory/containing/fonts/*。这是一个非常基于shell的方法,因此非常慢,并且也是非递归的。如果存在以匹配字符开头的文件,则只会创建目录。

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的方法以及类似的字符串操作,使用bash参数扩展,该方法将是递归的,并且比纯shell版本要快一些:

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

这也起作用。即使是大写和小写字母。
Parto

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'

我真的很喜欢,这正是我希望使用find所掌握的方法。有没有办法只创建大写的目录名,并将小写的文件名移入其中?
Arronical

1
我添加了另一个tr转换为大写。
xn。

为什么绕道而行xargs只是为了bash再次打电话?将输出传递find到while循环中并read逐条记录会更简单,更易读吗?
David Foerster'1

好问题,@ DavidFoerster。我猜想偏向于单线循环和偏向于一种更实用的方法。但是字符串中的bash脚本也不是很优雅,因此我想说while循环版本(bit.ly/2j2mhyb)可能更好。
xn。

顺便说一句,你可以避开讨厌的{}替代,如果你让xargs附加参数,然后参考$1shell脚本里面,例如:xargs -0 -n1 -- bash -c 'dir=/dest/$(basename "$1" | ...); ...; mv "$1" "$dir"' _。(请注意决赛_!)
David Foerster's
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.