根据文件扩展名对文件进行排序

根据文件扩展名对文件进行排序

我制作了一个脚本,它将根据文件的扩展名对文件进行排序并将它们放置在正确的文件夹中。例如,放置abc.jpg在目录中jpg

#!/bin/bash
#this script sorts files according to their extensions
oldIFS=$IFS
IFS=$'\n'
(find . -type f) > /tmp/temp
for var in `cat /tmp/temp`
do
name=`basename "$var"`
ext=`echo $name | cut -d'.' -f2- | cut -d'.' -f2- | cut -d'.' -f2- | cut -d'.' -f2- | cut -d'.' -f2- | cut -d'.' -f2- | cut -d'.' -f2-`
mkdir -p $ext
mv "$var" $ext/ 2> /dev/null
done
IFS=$oldIFS

这个脚本的问题:

  1. 涉及到IFS的使用,据说尽量避免使用IFS
  2. 它不会对没有文件扩展名的文件进行排序
  3. 它将在名为 bz 的文件夹中对像 abc.tar.bz 这样的文件进行排序,但是这样的文件应该放在 tar.bz 文件夹中
  4. 请参阅我的脚本的第 9 行;如果任何文件包含更多否。点(以其名称)比没有。cut -d'.' -f2-脚本中的of比 if 会导致文件名包含在扩展名部分中。
    例如,名为的文件i.am.live.in.india.and.i.study.computer.science.txt将被放置在名为的文件夹中study.computer.science.txt

您还可以建议任何调整,使这个脚本更小、更整洁。

答案1

递归到子目录

解析 的输出find是不可靠的。如果文件名中有换行符怎么办?使用find … -exec …,保证可靠的加工。

find . -type f -exec sh -c '…' {} \;

shell 片段接收$0.请注意,这是一个单独的 shell 进程,它不会从祖父母脚本继承变量或函数。您可以通过使用相同的 shell 子进程来处理多个文件来加快处理速度。

find . -type f -exec sh -c 'for x; do … done' _ {} +

这次,在循环内,文件名位于变量 中x

分解文件名

调用外部实用程序(例如sedcut等)很脆弱:您必须非常小心,以避免损坏某些文件名。您不需要这样:shell 的内置字符串处理功能足以满足您想要执行的操作。给定一个文件名$x

directory=${x%/*}
basename=${x##*/}
extension=…
if [ -n "$extension" ]; then
  mkdir -p "$directory/extension"
  mv "$x" "$directory/extension"
fi

扩展

文件的扩展名是什么?它是.名称中的其中之一之后的部分。没有标准说明是哪一个。在foo.tar.gz或之类的情况下,您可以决定什么是扩展名bar-1.2

下面是一些示例代码,它考虑要嵌套的常见压缩扩展名,并且要求扩展名包含字母,因此foo-1.2.tar.gz被认为具有扩展名tar.gz

extension=
while case "${basename##*.}" in
        gz|bz2|xz) extension=.${basename##*.}$extension;; # stackable extension
        *) false;;
do
  basename=${basename%.*}
done
case "${basename##*.}" in
  "$basename") :;; # no . ==> no extension
  *[!0-9A-Za-z]*) :;; # only allow alphanumeric characters
  *[A-Za-z]*) extension=${basename##*.}$extension;; # non-stackable extension
  *) false;; # require at least one letter
esac
extension=${extension#.}

答案2

虽然识别扩展的一般问题很困难,但您可以稍微清理一下脚本:

  1. 告诉find仅考虑具有扩展名的文件:-iname '*.*'
  2. 使用awk而不是cut自己尝试:
  3. 使用脚本,然后告诉find执行该脚本。

因此:一个名为的脚本move.sh

#! /bin/bash
for i
do
    ext=/some/where/else/$(awk -F. '{print $NF}' <<<"$i")
    mkdir -p "$ext"
    mv "$i" "$ext"
done

然后运行find

find . -name '*.*' -type f -exec move.sh {} +

这存在您无法在文件夹内重新排列的问题,因此您可以使用xargs

find . -name '*.*' -type f -print0 > /tmp/temp
xargs -0 move.sh < /tmp/tmp

我不太确定所涉及的效率,但另一种方法是获取所有扩展名,然后一次性移动所有涉及的文件。

就像是:

find . -name '*.*' -type f -print0 | sed -z 's/.*\.//g' | sort -zu > /tmp/file-exts

这应该会为您提供唯一文件扩展名的列表。那么我们的move.sh将会是这样的:

#!/bin/bash
for i
do
    mkdir -p "$i"
    find . -name "*.$i" -type f -exec mv -t "$i" {} +
done

我们将运行它:

xargs -0 move.sh < /tmp/file-exts

我在这篇文章中做了很多假设,例如sedsort支持-z(允许它们与 NUL 终止的线路一起工作findxargs蓬勃发展)。

相关内容