AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 522367
Accepted
SciGuy
SciGuy
Asked: 2019-06-02 10:16:30 +0800 CST2019-06-02 10:16:30 +0800 CST 2019-06-02 10:16:30 +0800 CST

递归地根据文件名(带空格)对文件进行排序和 mv

  • 772

我犯了一个错误,将文件一起转储到同一个目录中。幸运的是,我可以根据文件名对它们进行排序:''' 2019-02-19 20.18.58.ndpi_2_2688_2240.jpg '''在这种特定情况下, #位或2是位置信息。范围是 0-9 并且文件名的长度都相同,因此数字将始终位于文件名的相同位置(第 26 个字符包含空格,两侧是下划线)。我找到了这个很棒的链接: 通过仅搜索部分名称来查找文件的命令?

但是,我无法将输出通过管道传输到 move 命令中。我试图将输出循环到一个变量中,但这似乎也不起作用:

for f in find . -name '*_0_*' ; do  mv "$f" /destination/directory ; done

基于此链接,我可能将 * 或一些引号放在了错误的位置:mv: cannot stat No such file or directory in shell script。

也就是说,我有很多目录,我想将它们分类到其他地方的相同目录结构中:

-Flowers (to be sorted)          -Flowers-buds               -Flowers-stems
     -Roses                          -Roses                      -Roses
        buds.jpg       ===>             buds.jpg     ===>           stems.jpg
        stems.jpg
        petals.jpg
     -Daisies                       -Daisies                    -Daisies
        buds.jpgs                       buds.jpg                   stems.jpg
        stems.jpg
        petals.jpg
     -Tulips                        -Tulips                     -Tulip
        buds.jpgs                       buds.jpg                  stems.jpg
        stems.jpg
        petals.jpg

...以及更多基于该数字(#)。这是在 bash 中实用的操作吗?我在安装了 coreutils 的终端中运行 MacOS,因此这些工具的行为应该像 GNU linux,而不是 darwin (BSD)。

shell-script rename
  • 2 2 个回答
  • 1523 Views

2 个回答

  • Voted
  1. Gilles 'SO- stop being evil'
    2019-06-02T12:28:10+08:002019-06-02T12:28:10+08:00

    您可以使用findand来执行此操作mv,但这不是这里最简单的方法。你在 macOS 上,所以zsh是预装的。Zsh 带有一个简洁的文件批量重命名工具,称为zmv. 在终端中运行zsh,然后在 zsh 中运行类似:

    autoload zmv
    zmv -n '[^_]#_([0-9])_*' '/elsewhere/${1}/${f}'
    

    说明:

    • -n告诉zmv显示它会做什么,但实际上不做任何事情。当您对它显示的内容感到满意时,再次运行该命令而不使用-n.
    • 第一个非选项参数是通配符模式。zmv将作用于匹配的文件(并忽略不匹配的文件)。
    • [^_]#表示除 之外的零个或多个字符_。Zsh 使您可以访问与 bash 相同的通配符等。#是一个 zsh-only 功能(在 bash 中可用不同的语法),意思是“任意数量的前一个字符”。该模式[^_]#_[0-9]_*匹配任何包含两个下划线之间的单个数字且在该数字之前没有其他下划线的文件名。
    • 周围的括号[0-9]使数字$1在替换文本中可用。
    • 替换文本可以使用${1},${2}等来指代源模式中带括号的片段,并${f}指代完整的原始名称。

    例如,上面的代码片段2019-02-19 20.18.58.ndpi_2_2688_2240.jpg移至/elsewhere/2/2019-02-19 20.18.58.ndpi_2_2688_2240.jpg. 您的问题没有非常精确地描述需要移动的内容以及需要移动到的位置,但是您应该能够通过调整上面的命令来获得所需的内容。如果您无法弄清楚,请编辑您的问题以添加必须匹配和不得匹配的具体示例。

    如果子目录中有文件,则可以*/在模式的开头使用。如果你需要匹配目录来引用它,你需要在: 周围加上括号,你不能在斜杠周围加上括号。如果您想递归地遍历目录并在任何债务处匹配文件,请使用递归globbing。作为一个例外,您需要使用访问替换文本中的目录路径为. 在这种情况下,不使用括号通常更容易,而是使用修饰符将原始路径的部分提取为一个整体。例如,进行与上述相同的重命名,但将文件从当前目录移动到下的并行结构${NUM}***/(**/)${NUM}${f}/elsewhere, 但在文件名之前有一个额外的级别0,等:1

    autoload zmv
    zmv -n '**/[^_]#_([0-9])_*' '/elsewhere/${f:h}/${1}/${f:t}'
    

    如果很难根据文件名称匹配文件,另一种方法是根据更改时间来匹配它们¹。如果您只是将一堆文件复制到一段时间未更改的目录中,则新文件是最近更改时间的文件。您可以通过在 zsh 中使用glob 限定符c更改时间来匹配文件。例如,列出过去一小时内最后更改的文件:

    ls -lctr *(ch-1)
    

    您可以使用 glob 限定符(通过增加 ctime 进行排序)和(仅保留前N个匹配项)来引用N个最近更改的文件,而不是查找截止时间。例如,如果您知道您刚刚将 42 个文件移动到该目录中,并且此后没有更改任何内容:oc[1,N]

    ls -lctr *(oc[1,42])
    

    如果您想将 glob 限定符与 一起使用zmv,则需要传递该-Q选项。例如,要将文件移动到基​​于文件名称的目录,但忽略过去一小时内未更改的所有文件:

    zmv -n -Q '[^_]#_([0-9])_*(ch-1)' '/elsewhere/${1}/${f}'
    

    zmv有一些安全措施可以检查它是否不会覆盖文件,并且没有冲突(两个源文件具有相同的目标名称)。如果您有大量文件,这些安全措施可能需要一些时间。如果zmv太慢并且您的重命名结构保证不会发生任何冲突,您可以在循环中对您正在执行的特定重命名进行硬编码。即使您不使用 Zsh,它也倾向于击败 bash,zmv这要归功于它更好的globbing和参数扩展机制。为了提高性能,您可以动态加载包含用于文件操作的内置函数的模块。

    例如,将常规文件从当前目录下移动到一个全新的层次结构,如果它们的名称包含_0_:

    zmodload -m -F zsh/files 'b:zf_*'
    for x in **/*_0_*(.); do
      zf_mkdir -p /destination/directory/$x:h
      zf_mv ./$x /destination/directory/$x
    done
    

    (:h是另一个 zsh 功能:历史修饰符。)

    要获取文件名中两个下划线之间的第一个数字,并将文件放在以该数字命名的目录中,您需要提取该数字。zmv为带括号的组自动执行此操作,这里我们需要手动执行。

    zmodload -m -F zsh/files 'b:zf_*'
    for source in **/*_<->_*(.); do
      suffix=${${source:t}#*_<->_}
      n=${${source%$suffix}##*_}
      destination=${source:h}/$n/${source:t}
      zf_mkdir -p /destination/directory/$destination:h
      zf_mv ./$source /destination/directory/$destination
    done
    

    如果您想了解其他批量重命名文件的方法,可以浏览此站点上标记的问题rename。

    ¹文件的 inode 更改时间,简称“更改时间”或“ctime”,是文件最后一次移动或最后一次更改其属性(例如权限)的时间。它不同于修改时间(mtime)。

    • 3
  2. Best Answer
    Oh My Goodness
    2019-06-02T11:11:32+08:002019-06-02T11:11:32+08:00

    外壳在空格上拆分输入。您可以使用带有递归的 bash globbing**来正确拆分文件名:

    shopt -s globstar
    for f in **/*_0_*; do mv "$f" /dest/; done
    

    如果他们都去同一个地方,则不需要循环:

    shopt -s globstar
    mv **/*_0_*  /dest/
    

    ... 或 use find -exec,它将文件名直接从 find 传递给exec()调用,而不需要 shell 参与:

    find . -name '*_0_*' -exec mv {} /dest/ \;
    

    或使用read加号find -print0拆分为空。对于这个问题来说,这是矫枉过正的,在通配时很有用并且find -exec不能胜任任务:

    while IFS=  read -r -d ''; do mv "$REPLY" /dest/; done < <( find . -name '*_0_*' -print0 )
    

    ${var#remove_from_start}要根据文件名更改顶级目标,如您的示例所示,请使用和切断文件名的一部分${var%remove_from_end}。删除模式中的通配符将尽可能少地删除;要尽可能多地删除,请将操作字符加倍,如${…##…}or ${…%%…}:

        shopt -s globstar
        for f in **/*_0_*; do            # Flowers/Roses/stems.jpg 
          file=${f##*/}                  # stems.jpg
          base=${file%.*}                # stems
          path=${f%/*}                   # Flowers/Roses
          toppath=${path%%/*}            # Flowers
          subpath=${path#*/}             # Roses
          dest=$toppath-$base/$subpath/  # Flowers-stems/Roses/
          [[ -d $dest ]] || mkdir -p "$dest"
          mv "$f" "$dest"
        done
    
    
    • 2

相关问题

  • 打印文件行及其长度的脚本[关闭]

  • 根据其中的值批量重命名(附加)CSV 文件

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 按分隔符拆分并连接字符串问题

  • MySQL Select with function IN () with bash array

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve