使用排序将文件名中的单词按字母顺序排序?


8

在阅读有关bash中的批量重命名文件的教程并使用sort命令对文件内容进行排序时,我还无法弄清楚如何将两者结合起来。

我有一个目录,该目录的内容使用文件名中的标签进行排序,类似于TagSpaces程序处理事物的方式。创建或下载文件名时,我会在文件名的末尾添加任何我能想到的标签。这是一个例子:

Sunrise (2) #wallpaper #4k #googleimages.jpg

现在,我想浏览所有这些文件并重命名它们,以便标签按字母顺序排序,而不会影响标签之前或之后的任何内容(例如,图片的标题或文件扩展名)。因此,以上内容将变为:

Sunrise (2) #4k #googleimages #wallpaper.jpg

我该如何完成?我什至不知道如何将文件而不是其内容传递给类似的命令sort,然后可以将其输出传递给mv


不是您问题的答案,但是您应该在从命令行或脚本中将标签添加到文件(PDF)和处理中后,发布有关TagSpaces的答案-乍一看似乎与该问题非常相关。
cas

Answers:


5
#!/bin/bash

for i in dir_for_files/*; do
    filename=${i%%#*}
    sorted_tags=$(grep -o "#[^ .]*" <<< "$i" | sort -n | tr '\n' ' ')
    ext=${i#*.}
    echo mv "$i" "${filename}${sorted_tags% }.$ext"
done

测试:

##### Before sorting #####    
$ ls -1 dir_for_files
Note (3) #textfile #notes #important.txt
Sunrise (2) #wallpaper #4k #googleimages.jpg
Sunset (2) #wallpaper #2k #images.jpg

$ ./sort_tags.sh

##### After sorting #####
$ ls -1 dir_for_files
Note (3) #important #notes #textfile.txt
Sunrise (2) #4k #googleimages #wallpaper.jpg
Sunset (2) #2k #images #wallpaper.jpg

3

如果您有基于perl的代码renameprename在某些系统上),则可以使用perl对代码进行分割+排序。例如,给定

$ ls *.jpg
Sunrise (2) #wallpaper #4k #googleimages.jpg

然后(进行一些丑陋的处理以删除并替换.jpg后缀)

$ rename -v 'my @F = split / #/, substr($_,0,-4); $_ = (join " #", shift @F, sort @F) . ".jpg"' *.jpg
Sunrise (2) #wallpaper #4k #googleimages.jpg renamed as Sunrise (2) #4k #googleimages #wallpaper.jpg

检查

$ ls *.jpg
Sunrise (2) #4k #googleimages #wallpaper.jpg

可能还有很多改进的空间-但我希望它能给您一些想法。


3

zsh

autoload zmv # best in ~/.zshrc
zmv -n '([^#]#)(\#*)(.*)' '$1${(j: :)${(os: :)2}}$3'

(如果需要,请删除-n(空运行))。

  • [^#]#:0个或多个非#字符(#类似于正则*表达式)
  • s: : 在空间上分裂
  • o:顺序(排序)
  • j: ::加入空间。

因此,我们在空间的第一个#(包括)和最后一个.(排除)之间划分了部分,对结果列表进行排序,然后我们将其与空间结合在一起。

递归地:

zmv -n '(**/)([^#]#)(\#*)(.*)' '$1$2${(j: :)${(os: :)3}}$4'

要在标签名称中留出空格,我们可以分割#并修剪尾随空格,排序并加入#

zmv -n '([^#]#\#)(*)(.*)' '$1${(j: #:)${(os:#:)2}%% #}$3'

(#qD)如果您还想处理隐藏文件(Dot文件)或要处理隐藏目录中的文件,请添加一个glob限定符。


2

好问题!

这是我的简单bash脚本:

for file in *.jpg; do 
    afile=( ${file#*)} ); 
    echo mv "$file" "${file%%#*}$(echo $(sort<(printf "%s\n" "${afile[@]%%.*}"))).jpg";
done

说明:

  • afile=( ${file#*)} );我们的字符串转换为阵列。在这种状态下,除非您引用字符串,否则外壳程序将使用空格进行单词拆分。

    ${file#*)}cut-up-first-prefix)中:我们正在剥离从字符串开头到首次)看到使用的所有内容shell parameter expansion,因为#wallpaper #4k #googleimages.jpg考虑到file="Sunrise (2) #wallpaper #4k #googleimages.jpg"

  • ${file%%#*}切到最后一个后缀)中; 剥线从头到尾乞求到最后#看到为止。这将导致Sunrise (2)

  • ${afile[@]%%.*}cut-to-last-suffix)中:与上面相同,剥离从结尾开始,直到字符串的乞讨(在每个索引数组中),直到最后一次.看到为止。这将导致#wallpaper #4k #googleimages,我们也可以使用${afile[@]%.*}更好的!

  • 在中printf "%s\n" "${afile[@]%%.*}":我们用换行符打印数组元素([@]用于索引数组),(为什么用换行符?因为我们将对它们进行排序,因此应该将元素拆分为换行符)

  • 在中,$(sort<(printf "%s\n" "${afile[@]%%.*}"))我们正在对元素(或标签)进行排序。

  • $(echo $(sort<(printf "%s\n" "${afile[@]%%.*}"))):与上述相同的,但我们使用额外echo命令收集已排序的元素插入到一个线性的。

    使用double xargslike 也可能相同... |xargs -n1| sort | xargs
    请参阅以下示例,以更好地理解此步骤:

    echo -e "C\n4\nB" |sort
    4
    B
    C
    echo $(echo -e "C\n4\nB" |sort)
    4 B C

最后,mv命令将重命名$file为修改后的名称。

PS:删除echo mv ...在前面mv到出口空转,并执行实际的重命名。


1

老实说,这似乎有点复杂。这不是不可能的,但是我认为,使用此处用于“真实”编程的语言要好得多。Bash解决方案可能无法维护。(实际上,我没有试图侮辱仅使用Bash的解决方案,我认为重命名魔术非常神奇)

就是说,这是Ruby的解决方案。您可以将其写入文件,然后只要已安装Ruby,就可以从您的Shell中执行该文件。

#!/usr/bin/env ruby
Dir.glob('*.jpg').each do |filename|
    # `name` contains the name of the file, without the tags. `tags` is an array
    # of all tags of the file.
    name, *tags = filename.split(' #')
    # if there aren't any tags, just skip the file.
    if tags.empty?
        next
    end
    # remove the trailing '.jpg' and sort all the tags
    tags.last.gsub!(/\.jpg$/,'')
    tags.sort!
    tags = [name] + tags
    # finally, move the file to the correct location with sorted tags.
    File.rename filename, "#{tags.join(' #')}.jpg"
end

要执行脚本,只需将其放置在图像所在的目录中即可。此解决方案应具有很强的弹性,以应对愚蠢的图像名称和C#不会引起问题的标记。也就是说,请确保在运行脚本之前进行备份。行动行动可能与行动一样具有破坏性rm

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.