如何在 bash 下递归更改文件和文件夹的大小写

如何在 bash 下递归更改文件和文件夹的大小写

我有一些文件和文件夹,它们全都是大写,我想将它们重命名为小写。在 Linux 系统的 bash 中执行此操作的最佳方法是什么?

举个例子,我可能有:

.
|-- FOLDER0
|   |-- SUBFOLDERA
|   `-- SUBFOLDERB
`-- FOLDER1
    `-- AFILE.TXT

我想将其转换为:

.
|-- folder0
|   |-- subfoldera
|   `-- subfolderb
`-- folder1
    `-- afile.txt

我可能可以编写一个深度优先递归脚本来执行此操作(深度优先确保文件和子文件夹在其父文件夹之前重命名),但我想知道是否有更好的方法。rename可能有用,但它似乎不支持递归。

答案1

find . -depth -print0 | xargs -0 rename -n '$_ = lc $_'

一旦您确定它正在执行您想要的操作,请取出 -n 标志。

答案2

这更像是对这个论坛运作方式的评论和元观察,但我强烈认为应该更大声地表达出来,而不是在 rudedog 的回答中发表评论。这也是一条针对可能有问题的人的评论相似的回答 David Dean 的问题,但并不完全相同。

使用重命名文件的递归操作很容易造成大量破坏。我再说一遍,因为这非常重要。使用重命名文件的递归操作可以非常非常快地破坏大量数据。您可以快速使系统完全无法使用。您可以非常快速地删除 99% 的数据。应该采取此类行动非常仔细并经过深思熟虑、备份和测试运行。

我是非常对这个特定问题的答案持谨慎态度,因为它依赖于几个特定的默示假设:

  1. 我使用的“rename”版本与您使用的版本相同,并且它们都具有我习惯的安全功能。据我所知,“rename”不是 unix 中的标准、常规实用程序。它似乎是某些发行版中存在的一些巧妙脚本。
  2. 如上所述,“重命名”具有一些防止覆盖文件的安全功能。
  3. 要么被混合的文件具有明确的压缩大小写结果,要么如果生成的子目录中有一些大写文件也没什么大不了的,因为压缩大小写结果发生了冲突,并且如果目标名称已经存在,则名称更改机制不执行任何操作。

我完全可以接受走捷径。但我试着去理解,有些事情一条捷径,以及这种捷径的局限性是什么,这样我知道什么时候需要运行始终正确的程序,或者快 10,000 倍但在某些边缘情况下失败的捷径。

简而言之——上述使用重命名的脚本在 90% 的情况下是 100% 合理的。在 100% 的情况下 100% 正确的解决方案实际上有点复杂,需要一些智慧来处理命名空间冲突。

但正如我之前所说,非常谨慎对待递归文件操作,尤其是当它们只处理权限或文件名等元数据时。它们速度很快,而且如果不备份就很难逆转……

以下是如何在 bash 或 ksh 或其他具有“typeset”的现代 shell 中编写“重命名”命令的示例。我们将此脚本命名为 /tmp/squash

#!/usr/local/bin/pdksh
# use some shell that supports typeset such as ksh, pdksh, or bash
# call this script from "xargs -0" and feed it with "find -print0"

typeset -l targetname || { echo "This shell doesn't support typeset!" >&2 ; exit 1 ; }
for file
do
  count=""
  target="$file"
  targetpath="${target%/*}/"
  targetname="${target##*/}"
  if
    [[ "$file" == "$targetpath$targetname" ]]  || [[ "$file" == . ]]
  then
    :
  else
    while
      test -e "$targetpath/$targetname""$count"
    do
      count=$((${count:-0}+1))
    done
    echo "renaming $file to $targetpath$targetname$count" >&2
    mv -n  "$file" "$targetpath$targetname$count"
  fi
done

现在,您可以运行以下两个命令并将所有文件压缩,并且不会覆盖任何文件。

find /path/to/files -type f -print0 | xargs -0 /tmp/squash
find /path/to/files -type d -depth -print0 | xargs -0 /tmp/squash

并且我假设在运行程序时,相关目录中不会放入任何内容或重命名任何内容。

答案3

我将听从其他人的回答,但 -type f 和 -type d 作为 find 的参数将分别返回文件和目录。如果您想先更改文件,然后更改目录...那么请先更改文件,然后更改目录。

相关内容