【问题标题】:How to rename files recursively to hex base 16如何将文件递归重命名为十六进制基数 16
【发布时间】:2023-03-22 15:25:01
【问题描述】:

如何将不同文件夹中的文件(但所有文件夹都在一个文件夹下)(不考虑它们的名称)递归地重命名为十六进制基数 16?

所以,当它进入下一个文件夹时,它会记住它之前的命名并从那里继续。


例如(与截图不同):
文件夹 1:1.jpg、1_A.jpg、... 10th_blah.jpg
文件夹 2:1.jpg,...,2000th_text1.jpg

会变成->
FolderAllTogether: 1.jpg, 2.jpg, ..., A.jpg, B.jpg, ..., 7D0.jpg


解决方案可以是 javascript、bash、python 或任何其他我可以在 macOS 上轻松运行的脚本语言。


我的代码:

const fs = require('fs')
const path = require('path');
const dir = path.resolve(__dirname, 'ImagesSuperTemp');
let i = 0

function renameFilesRecursive(dir, from, to) {

    fs.readdirSync(dir).forEach(it => {
        const itsPath = path.resolve(dir, it);
        const itsStat = fs.statSync(itsPath);

        if (itsPath.search(from) > -1) {
            console.log(`${i} : rename ${from} to ${to}`);
            fs.renameSync(itsPath, itsPath.replace(from, to))
            i = i + 1
        }

        if (itsStat.isDirectory()) {
            renameFilesRecursive(itsPath.replace(from, to), from, to)
        }
    })
}

renameFilesRecursive(dir, /\*.jpg$/, `${i.toString(16)}.jpg`);

但它什么也没做!

【问题讨论】:

  • 基本上,您只需获取所有文件的完整列表,创建一个计数器(可以是 int),遍历文件并在将文件重命名为计数器十六进制值后递增该计数器
  • 你是否可以访问 Perl 的独立 renameprename
  • @Cyrus 我可以安装它。它是否正确? learn.perl.org/installing/osx.html
  • 您还可以使用命令brew install rename 使用 homebrewmacOS 上安装 Perl 的独立 rename

标签: javascript python bash macos


【解决方案1】:

下面有一个 bash 脚本。 更改dest_dirsource_dir的值

不要忘记使用chmod +x flattener.sh使其可执行

#!/bin/env bash

dest_dir="/var/tmp/flat-files"
source_dir="parent-dir"

if [ ! -d "${dest_dir}" ] ; then
  mkdir -p "${dest_dir}"
fi

if [ ! -f "${dest_dir}"/index.dat ] ; then 
  printf "1" > "${dest_dir}"/index.dat || exit 1
fi

find "${source_dir}" -type f -print0 |\
  xargs -0 bash -c ' \
    for filename ; do \
      index=$(cat '"${dest_dir}"'/index.dat) ;\
      printf "%d" $((index+1)) > '"${dest_dir}"'/index.dat ;\
      if [[ "${filename}" =~ [.] ]] ; then \
        extension=."${filename##*.}" ;\
      else \
        extension="" ;\
      fi ;\
      mv -vu "${filename}" '"${dest_dir}"'/$(printf "%X" $index)"${extension}" ;\
    done' flattener

【讨论】:

  • tnQ。我看到您正在通过移动文件mv 重命名。有没有办法不触摸/移动原始文件,但在目标文件夹中有新的重命名文件?
  • 复制文件将mv改为cp
  • 成功了!只是想让您知道 index.dat 显示 145048 但是,第一个文件名是 1,最后一个文件名是 23697,即 145047。
  • index.dat 包含下一个可用的十进制索引。所以,我认为这没问题。
【解决方案2】:

Python 解决方案

(如果你想重命名一个已经存在的文件,会抛出一个错误,例如你正在处理图像 5.jpg,而你的计数器是 3,但是 3.jpg 已经存在;可以用try@来避免这种情况987654323@ 子句,但您的图片并不总是以 1 递增 - 不确定您需要什么):

import os
counter = 1
for r, d, f in os.walk(r"Images"):  # can also put a full path here
    for name in f:
        # first argument here is full path to the image
        # second argument is the path + the hex number of the counter + the extension of the file
        os.rename(r + os.sep + name, r + os.sep + hex(counter).lstrip("0x") + os.path.splitext(name)[-1])
        counter += 1

编辑

我进一步记录了代码,添加了一条状态消息并注意到您需要大写的十六进制文件名:

import os
counter = 1
for r, d, f in os.walk(r"Images"):  # can also put a full path here
    for name in f:
        # full path to original image
        original_fn = r + os.sep + name

        # path + hex number of counter + extension of file
        new_fn = r + os.sep + hex(counter).lstrip("0x").upper() + os.path.splitext(name)[-1]  

        # rename, status print, incrementing counter
        os.rename(original_fn, new_fn)
        print(f"Renamed {original_fn} > {new_fn}")
        counter += 1

编辑 2

这是一种解决方案,首先将所有文件重命名为附加随机字符串的临时文件,然后根据计数器将所有文件重命名为十六进制数字。希望能解决您的问题:

import os
import random
import string

for r, d, f in os.walk(r"Images"):
    for name in f:
        rnd_string = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(12))
        original_fn = r + os.sep + name
        new_fn = r + os.sep + rnd_string + name
        os.rename(original_fn, new_fn)
        print(f"Temp. rename {original_fn} > {new_fn}")

counter = 1
for r, d, f in os.walk(r"Images"):  # can also put a full path here
    for name in f:
        original_fn = r + os.sep + name
        new_fn = r + os.sep + hex(counter).lstrip("0x").upper() + os.path.splitext(name)[-1]  
        os.rename(original_fn, new_fn)
        print(f"Final rename {original_fn} > {new_fn}")
        counter += 1

【讨论】:

  • 如果我一次又一次运行它会删除一批文件!
  • @Dr.jacky 我没有看到任何文件被删除的迹象,代码也不允许这样做 - 如果重命名失败(由于文件已经存在)会发生什么是它抛出一个异常并且代码停止。看起来您多次执行了代码,我不完全理解。如果您想进行试运行,只需将包含 os.rename(original_fn, new_fn) 的行注释掉即可。
  • 但是,不仅在终端上,在文件夹中,我一次又一次地运行它命令后丢失了文件。如果你愿意,我可以截图。但无论如何,是否有任何解决方案不关心它们是否已经重命名?如果是,让我们再做一次 - 除了一次又一次地运行命令之外,有可能在其他文件夹中,有一个文件具有十六进制基 16 名称。
  • @Dr.jacky 我编辑了我的答案以提供一个解决方案,首先将所有文件重命名为临时名称,然后根据您的计数器将临时名称重命名为十六进制值 - 希望能解决您的问题。如果之前的代码减少了文件总数,我也会感到惊讶。
【解决方案3】:

使用bash >= 4.0 和cp(没有mv/rename):

shopt -s globstar
unset int
for i in **/*.jpg; do
  ((int++))
  printf -v hex '%x' "$int"
  echo cp -v "$i" "FolderAllTogether/${hex^^}.jpg"
done

如果输出看起来没问题,删除echo

【讨论】:

  • line 1: shopt: globstar: invalid shell option name line 6: FolderAllTogether/${hex^^}.jpg: bad substitution
  • 我已经更新了我的答案。您需要 bash 版本 >= 4.0。
【解决方案4】:

我只是为了完整起见而添加此内容,以回应您要求重命名文件的实际问题,尽管它表明您实际上想要复制文件。

你可以像这样用 Perl rename 做到这一点:

find images -iname "*.jpg" -print0 | rename --stdin -n -0 -N 01 '$_=sprintf("%X.jpg", $N);'

样本输出

'images/11/5.jpg' would be renamed to '1.jpg'
'images/11/3.jpg' would be renamed to '2.jpg'
'images/6/2.jpg' would be renamed to '3.jpg'
'images/6/3.jpg' would be renamed to '4.jpg'
'images/1/17.jpg' would be renamed to '5.jpg'
'images/1/21.jpg' would be renamed to '6.jpg'
'images/1/22.jpg' would be renamed to '7.jpg'
'images/1/23.jpg' would be renamed to '8.jpg'
'images/1/1_A.jpg' would be renamed to '9.jpg'
'images/1/25.jpg' would be renamed to 'A.jpg'
'images/1/1.jpg' would be renamed to 'B.jpg'

跨目录保留计数器的关键是使用-N 01,然后您就可以使用自动递增计数器。


如果您有 bash 4 或更新版本或 zsh(我认为),您可能可以使用这个更简单的版本:

rename -n -N 01 '$_=sprintf("%X.jpg", $N)' images/**/*.jpg

请注意,Perl rename 最简单地安装在 ma​​cOS 上,使用 homebrew 使用:

brew install rename

【讨论】:

    【解决方案5】:

    使用bashfind

    #!/bin/bash
    
    srcdir='/path/to/source/directory'
    dstdir='/path/to/destination/directory'
    
    n=1
    while IFS= read -r -d '' file; do
        while [[ -e $dstdir/$(printf '%X' $n).jpg ]]; do ((++n)); done
        cp -- "$file" "$dstdir/$(printf '%X' $n).jpg"
    done  < <(find "$srcdir" -type f -name '*.jpg' -print0)
    

    如果源目录和目标目录在同一个文件系统上,您可以考虑将cp 替换为ln

    【讨论】:

    • batch-renamer-hex3.sh: line 10: syntax error near unexpected token batch-renamer-hex3.sh: line 10: done
    • @Dr.jacky 你是如何运行脚本的?
    • 将代码放在 fileExample.sh 中,然后像这样运行它:sh fileExample.sh
    • @Dr.jacky 这不是运行 bash 脚本 的正确方法。您应该将其作为bash fileExample.shchmod +x fileExample.sh 运行,然后./fileExample.sh 您将其作为标准shell 脚本运行。
    • @Dr.jacky 顺便说一句,我已经编辑了脚本以不覆盖现有文件,尽管例如,当脚本的两个实例同时运行时,它仍可能调用竞争条件。
    猜你喜欢
    • 1970-01-01
    • 2017-03-14
    • 2019-11-07
    • 1970-01-01
    • 2014-01-25
    • 2020-07-04
    • 2014-04-18
    • 1970-01-01
    • 2019-01-23
    相关资源
    最近更新 更多