【问题标题】:Shorten filename to n characters while preserving file extension将文件名缩短为 n 个字符,同时保留文件扩展名
【发布时间】:2020-06-26 10:02:11
【问题描述】:

我正在尝试在保留扩展名的同时缩短文件名。

我认为cut 可能是最好的工具,但我不确定如何保留文件扩展名。

例如,我正在尝试将 abcdefghijklmnop.txt 重命名为 abcde.txt

我想简单地去掉文件名的末尾,以便总字符长度不超过 [在本例中] 5 个字符。

我不担心文件名冲突,因为我的数据集可能不包含任何内容,无论如何我会在重命名之前查找、分析文件并进行测试。


这最终的背景是我想批量截断超过 135 个字符的文件名,以便我可以将文件重新同步到 Synology NAS 上的加密共享。

我找到了一种搜索所有超过 135 个字符的文件名的好方法: find . -type f | awk -F'/' 'length($NF)>135{print $0}'

我想通过管道将它传递给一个简单的剪切命令,以将文件名缩小到合适的大小。也许有比这更好的方法。我找到了shorten filenames while preserving extensions的方法,但我需要递归所有子目录。

任何帮助将不胜感激,谢谢!

更新说明:

我想使用语法如下的单行:

find . -type f | awk -F'/' 'length($NF)>135{print $0}' | some_code_here_to_shorten_the_filename_while_preserving_the_extension

【问题讨论】:

  • 是否担心您的缩短方法可能会导致文件名不唯一?
  • 好问题@jas,不,我不担心。使用我的数据集,不可能创建重复的名称。而且无论如何,我会提前找到所有长文件名并在运行脚本之前对其进行分析。
  • 您能否编辑您的问题以添加说明和/或伪代码来显示您希望如何重命名文件?您真的只想取基本名称的前五个字符 + 扩展名吗?
  • 谢谢@jas 我更新了问题。我正在尝试制作这样的单行:find . -type f -iname "*" |awk -F'/' 'length($NF)>135{print $0}' | some_code_here,以便它通过子目录递归并删除所有长文件名的结尾。
  • 注意-iname "*"没有效果;如果你实际上根本没有按文件名过滤,你可以写find -type f

标签: bash sed rename cut xargs


【解决方案1】:

使用 GNU 查找和 bash:

export n=10 # change according to your needs
find . -type f                      \
     ! -name '.*'                   \
       -regextype egrep             \
     ! -regex '.*\.[^/.]{'"$n"',}'  \
       -regex '.*[^/]{'$((n+1))',}' \
       -execdir bash -c '
    echo "PWD=$PWD"
    for f in "${@#./}"; do
        ext=${f#"${f%.*}"}
        echo mv -- "$f" "${f:0:n-${#ext}}${ext}"
    done' bash {} +

这将执行试运行,即显示文件夹,然后显示要在其中执行的命令。一旦你对它的输出感到满意,你可以在mv之前删除echo(如果你愿意,也可以删除echo "PWD=$PWD"行),它实际上会将所有名称超过n字符的文件重命名为与@完全相同的名称987654326@ 个字符长度,包括扩展名。

请注意,这不包括隐藏文件,以及扩展名等于或大于n 的文件(例如.hiddenapp.properties,其中n=10)。

【讨论】:

  • 这运行得很漂亮——尽管它确实错过了一个文件。我检查了结果,似乎该文件的文件名中有一个
  • 我正在尝试创建一个可以在终端中使用而不是脚本的单行代码。我正在接近这个,但它不太有效(假设所有扩展都是一个点和三个字符):find . -type f | awk -F'/' 'length($NF)>135{print $0}' | xargs -0 rename -n 's/(.*\/)(.{131}).*([.].*$)/$1$2$3/'
  • @john 好吧,您可以轻松地将其变成单行(很长),或者您可以将其保存到脚本中并直接使用。您提到的Wrt失败,如果您发布导致问题的文件名,我可以看看
  • 谢谢@oguz .. 有问题的文件名是:Governor Insists “No Insult Intended,” as “Blasphemy” Trial Begins.htm 我正在检查 SublimeText 中的文件名,双引号是倾斜的,而不是标准的直双引号。此外,我与find . -type f | awk -F/ 'length($NF)>135{print $0}' | sed -r 's_(.*/+)(.{0,131}).*([.].*$)_\1\2\3_' 非常接近,但可惜我对管道和参数感到困惑,因此研究如何在其中获得mv。感谢您的耐心和帮助。 :-)
  • 我创建了一个这样命名的文件,上面的脚本成功地将它重命名为 Govern.htm。不过我没看懂你的脚本
【解决方案2】:

使用 bash 字符串操作
详情:https://www.linuxtopia.org/online_books/advanced_bash_scripting_guide/string-manipulation.html.
滚动到“子串提取”

以下示例将文件名剪切为 10 个字符并保留扩展名

~ % cat test   

rawFileName=$(basename "$1")
filename="${rawFileName%.*}"
ext="${rawFileName##*.}"

if [[ ${#filename} < 9 ]]; then
    echo ${filename:0:10}.${ext}
else
    echo $1
fi

和测试:

~ % ./test 12345678901234567890.txt
1234567890.txt
~ % ./test 1234567.txt             
1234567.txt

【讨论】:

    【解决方案3】:

    更新

    由于您的文件分布在目录树中,您可以使用我原来的方法,但将脚本传递给 sh 命令传递给 find-exec 选项:

    n=5 find . -type f -exec sh -c 'f={}; d=${f%/*}; b=${f##*/}; e=${b##*.}; b=${b%.*}; mv -- "$f" "$d/${b:0:n}.$e"' \;
    

    原答案

    如果文件名在变量x 中,那么${x:0:5}.${x##*.} 应该可以完成这项工作。

    所以你可能会做类似的事情

    n=5 # or 135, or whatever you like
    for f in *; do
      mv -- "$f" "${f:0:n}.${f##*.}"
    done
    

    显然,这假设缩短的名称之间没有冲突。如果有冲突,那么only one would survive!所以要小心。

    【讨论】:

    • 抱歉,Enrico,我无法让您的回答发挥作用。
    • @john,我添加了一个可能的用例。
    • 我在我的测试集上尝试了find . -type f -exec sh -c 'f={}; d=${f%/*}; b=${f##*/}; echo mv -- "$f" "$d/${b:0:135}.${b##*.}"' \;,但它返回了sh: 1: Bad substitution
    • @john,如果您使用bash 而不是sh 会怎样?顺便说一句,我忘了删除echo(我用来打印命令而不是执行命令,只是为了安全起见)。
    • 如果我使用bash 而不是sh,那么它会缩短文件名(包括扩展名),然后再附加扩展名。例如aaaaa136.txt 变为aaaaa136.tx.txt
    猜你喜欢
    • 1970-01-01
    • 2015-06-18
    • 1970-01-01
    • 2023-03-29
    • 2018-09-28
    • 2021-04-11
    • 2014-06-20
    • 2017-03-12
    • 1970-01-01
    相关资源
    最近更新 更多