基于文件名对Bash中的271,568个文件进行排序

时间:2016-01-05 01:46:28

标签: linux bash file sorting filesystems

我有一个需要排序的271,568个文件的集合,所有这些文件都在同一个目录中。幸运的是,它们都根据它们应该在哪个文件夹中方便地命名。

例如,一小部分文件可能如下所示:

.
├── file.sort.shamwow
├── file.sort.shamwow.abc
├── file.sort.shamwow.example.alsoafile
├── file.sort.shamwow.example.file
├── foo.bar
├── foo.bar.a
├── foo.bar.b
├── foo.lel
├── foo.wow.a.50
└── foo.wow.b

完成排序后,它们应如下所示:

.
├── file
│   └── sort
│       └── shamwow
│           ├── example
│           │   ├── file.sort.shamwow.example.alsoafile
│           │   └── file.sort.shamwow.example.file
│           ├── file.sort.shamwow
│           └── file.sort.shamwow.abc
└── foo
    ├── bar
    │   ├── foo.bar
    │   ├── foo.bar.a
    │   └── foo.bar.b
    ├── foo.lel
    └── wow
        ├── foo.wow.a.50
        └── foo.wow.b

因此文件foo.wow.a.50将放在目录wow内的目录foo内,对所有文件都是如此。

我想要的程序会根据点到目录的位置对文件进行排序。但是,如果该文件夹中只有一个文件(例如foo/wow/a.50),则不会仅为该文件创建新文件夹。

现在,我的半功能脚本看起来像:

#!/bin/bash
#organization for gigantic folder

> foo.txt

for f in *; do
    d=${f:3}
    d=${d%%.*}
    d=${d%%.*}
    echo $d

    if grep -Fxq "$d" foo.txt
    then
        mkdir -p $d
        mv $f $d
    else
        echo $d >> foo.txt
    fi
done

rm foo.txt

但它并没有那么好用。

有人可以修复我的代码,还是自己来解决这个问题?谢谢!

1 个答案:

答案 0 :(得分:0)

忽略您的请求输出无法在文件系统上表示(需要相同的名称来引用文件和目录):

#!/bin/bash
#      ^^^^- must be bash shebang, must be shell 4.0 or newer

# first pass: count directory references
declare -A refcounts=( )
for f in *; do
  f_part=$f
  while [[ $f_part = *.* ]]; do
    refcounts[$f_part]=$(( ${refcounts[$f_part]} + 1 ))
    f_part=${f_part%.*}
  done
  refcounts[$f_part]=$(( ${refcounts[$f_part]} + 1 ))
done

# second pass: use that information
# ...this is some ugly code, but I don't have the time right now to make it simpler.
for f in *; do
  f_part=${f%%.*}
  f_rest=${f#*.}
  while : "f=$f; f_part=$f_part; f_rest=$f_rest"; do
    new_piece=${f_rest%%.*}
    [[ $new_piece ]] || break
    f_part_next=${f_part}.$new_piece
    f_rest_next=${f_rest#"$new_piece"}; f_rest_next=${f_rest_next#.}
    if [[ $f_rest = *.* ]] && (( ${refcounts[${f_part_next}]:-0} > 1 )); then
      f_part=$f_part_next
      f_rest=$f_rest_next
    else
      break
    fi
  done
  dest="${f_part//"."/"/"}/${f_rest}"
  mkdir -p -- "${dest%/*}"
  mv -- "$f" "$dest"
done