如何使用 find 和 awk 在 tcsh shell 脚本中引用文件名中的感叹号?

如何使用 find 和 awk 在 tcsh shell 脚本中引用文件名中的感叹号?

我有一个脚本,可以在文件名中搜索空格字符“ ”、感叹号“ !”和美元符号“ $”,并将每个字符替换为下划线“ _”。但是,它根本不处理带有感叹号的文件名。在当前文件夹和所有子文件夹中搜索要重命名的文件,直到一定深度(此处不太相关)。文件夹名称也应该以同样的方式更改,这就是深度交错的原因。

这是所需的重命名方案:

This is cool!.txt     -->    This_is_cool_.txt
Thank$.log            -->    Thank_.log
Foo 1/Bar 2/a.txt     -->    Foo_1/Bar_2/a.txt

这是脚本:

#!/bin/tcsh -f
  
foreach n ( 1 2 3 4 5 6 7 8 )
    find . -mindepth $n -maxdepth $n -name '*[ $\!]*' | fgrep -v \" | \
    awk '{printf "mv -i -- \"%s\" \"%s\"\n", gensub("!","\\\\\!","g",$0), gensub(" ","_","g",gensub("\\$","\\\\$","g",gensub("!","\\\\\!","g",$0)))}' | tcsh -cf
end 

\我改变了两个感叹号之前的反斜杠 ' ' 的数量,但没有效果。错误消息可能如下所示(偶数个反斜杠):

awk: cmd. line:1: warning: escape sequence `\!' treated as plain `!'

或者 shell 脚本(显然)打开一个交互式 shell?!有时什么也没有发生(我想当第一个参数中有太多反斜杠mv并且找不到文件时)。

PS:正如你所看到的,我排除了带有双引号“ "”的文件,因为我也无法让它们工作。如果您也能建议如何处理这些问题,那就更好了!

答案1

mv直接调用比合成命令awk并将其通过管道传输到 shell更容易

#!/bin/bash
#
find -mindepth 1 -maxdepth 8 -type f -name '*[ !$]*' -execdir
    bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +

此版本重命名文件,但不重命名目录(如问题中所述)。如果您实际上也想重命名目录,请使用此变体,

find -mindepth 1 -maxdepth 8 -depth -name '*[ !$]*' -execdir
    bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +

在这两种情况下,echo当您满意该命令将执行您所期望的操作时,请删除它。用于mv -i交互式重命名。中的下划线bash -c '...' _ {}成为在子 shell 中运行的代码的“名称”。它被映射到$0并将用于报告代码错误(如果有),因此您可以放在bash那里,甚至subshell- 它只是一个标签。

答案2

此脚本假设您不想限制 的深度find并且希望将所有字符替换$!"'_

由于问题不包含有关操作系统或工具的特定版本的信息,我尝试仅依赖 POSIX 规范,而不需要像 GNU 这样的特定版本。

#!/bin/sh
find . -depth -name '*[ $!"]*' | while IFS= read -r file
do
   dir="${file%/*}"
   newname="$(echo "${file##*/}" | tr ' $!"' '____')"
   echo mv "$file" "$dir/$newname"
done

该脚本仅经过轻微测试。如果它打印正确的mv命令,请删除echo.

解释:

find . -depth...确保文件在目录之前打印(如果两者都包含要替换的字符)。这样我们将首先重命名文件,然后重命名父目录。

父目录名称以“`分隔dir="${file%/*},保持不变。

文件名(或最后一个目录名)被提取并更改为newname="$(echo "${file##*/}" | tr ' $!"' '____')".


进一步改进

要处理文件名中的其他“奇怪”字符,最好直接从 的操作中调用循环体中的代码,find如下-exec所示罗埃马回答

find . -depth -name '*[ $!"]*' -exec sh -c 'for file in "$@" ; do dir="${file%/*}" ; newname="$(echo "${file##*/}" | tr '\'' $!"'\'' "____")" ; echo mv "$file" "$dir/$newname"; done' _ {} +

也可以看看https://mywiki.wooledge.org/BashFAQ/020

在另一个答案中使用-execdiras 可以避免拆分文件名和父目录,但 POSIX 未定义此操作。

可以用诸如 之类的外壳替换......echo组合。|tr"${newname//[ $!\"]/_}"

相关内容