覆盖find找到的文件?

覆盖find找到的文件?

我有一个文件列表

-rw-rw-r-- 1 t t 24813 Oct 23  2002 fig8_21.gif
-rw-rw-r-- 1 t t  2259 Oct 23  2002 fig8_21t.gif
-rw-rw-r-- 1 t t 35331 Oct 23  2002 fig8_23.gif
-rw-rw-r-- 1 t t  2610 Oct 23  2002 fig8_23t.gif
-rw-rw-r-- 1 t t 19970 Oct 23  2002 fig8_24.gif
-rw-rw-r-- 1 t t  2019 Oct 23  2002 fig8_24t.gif
-rw-rw-r-- 1 t t 54623 Oct 23  2002 fig8_3.gif
-rw-rw-r-- 1 t t  3657 Oct 23  2002 fig8_3t.gif
-rw-rw-r-- 1 t t 35861 Oct 23  2002 fig8_4.gif
-rw-rw-r-- 1 t t  2344 Oct 23  2002 fig8_4t.gif

我想<...>t.gif<...>.gif例如 copy fig8_4.gifto overwrite进行覆盖fig8_4t.gif

我首先<...>t.gif通过 找到这些文件find . -regex ".*t\.gif"。然后我想用basenamestrip the filenames,但是为什么会出现以下警告?

$ find .  -regex ".*t\.gif" -execdir basename {} \;  
find: The relative path `~/program_files/internet/SSH_tunneling/' is included in the PATH environment variable, which is insecure in combination with the -execdir action of find.  Please remove that entry from $PATH

$ find .  -regex ".*t\.gif" | xargs basename 
basename: extra operand ‘./f10_1t.gif’

我该如何继续完成我的任务呢?

可以不使用吗find

答案1

是的,这是可能的。一种方法可能如下所示:

cd /den/of/gifs && \
for f in ./*t.gif; do
    mv -- "${f%%t.gif}.gif" "$f"
done

${var%%pattern}是标准/POSIXsh语法,用于删除匹配的最长字符串图案从年底开始$var

答案2

为了使其更加紧凑和更快,您可以使用parallel

parallel mv {} {.}t.gif  :::: <(find . -regex '.+[0-9].gif')

after 的表达式::::提供了 的参数parallel。然后使用这些参数{}。表示{.}不带文件扩展名的参数。因此,在我们的例子中,{}将是不带 的文件的文件名t,例如fig8_4.gif.{.}那么就会是fig8_4

答案3

如果你想继续使用findbasename,这应该可行:

find . -name "*.gif" ! -name "*t.gif" -execdir sh -c '
  cp -- "$0" "$(basename "$0" .gif)t.gif"' {} \;

从性能和资源角度来看,这不是最佳选择。

答案4

关于您的错误:

find:相对路径‘~/program_files/internet/SSH_tunneling/’包含在PATH环境变量中,与find的-execdir操作结合使用是不安全的。请从 $PATH 中删除该条目

~/program_files/internet/SSH_tunneling/在你的$PATH.那是字面意思~。确实如此不是意思是你的主目录。相反,这意味着相对路径 ~/program_files/internet/SSH_tunneling/,即当前目录内的目录SSH_tunneling内。internetprogram_files~

-execdir不喜欢相对目录,$PATH因为它会chdir()进入要从中执行命令的目录。例如,如果在目录树中下降时,find找到一个gif文件,./some-dir其中也包含一个~目录,并且有一个./some-dir/~/program_files/internet/SSH_tunneling/basename,那么它最终可能会执行 basename

您可能有:

PATH="~/program_files/internet/SSH_tunneling/"

在你的~/.bash*其中应该是:

PATH=~/program_files/internet/SSH_tunneling/

或者

PATH="$HOME/program_files/internet/SSH_tunneling"
 $ find .  -regex ".*t\.gif" | xargs basename 
 basename: extra operand ‘./f10_1t.gif’

xargsbasename将使用尽可能多的参数运行。basename最多需要 2 个参数,第一个参数是路径,第二个参数是模式。所以你的命令没有意义。

另外,xargs默认情况下,需要在输入上使用空白(空格、制表符以及可能更多,具体取决于您的区域设置和实现find)和换行符(所有这些都可能出现在文件名中)分隔列表,并对引号进行特殊处理,这不是格式输出经过find

find将输出类似:

./path/to/picture from "The End of the World".gif

xargs将被理解为 3 个不同的参数:./path/to/picture, from, The End of the World.giffind没有模式以 预期的格式输出xargs,此处:

./path/to/picture\ from\ \"The\ End\ of\ the\ World\".gif

或者

'./path/to/picture from "The End of the World".gif'

某些xargs实现确实支持-0处理 NUL 分隔列表而不是空白/换行符+引号的选项,并且某些find实现具有-print0谓词来输出具有该格式的文件名(对于那些不支持的实现,您可以使用-exec printf '%s\0' {} +它来代替)。

所以像这样:

find .  -name "*t.gif" -print0 | xargs -r0 -I {} basename {} t.gif

这对你的总体目标没有多大用处。相反,你可能想要类似的东西:

find . -name '*t.gif' -type f -exec sh -c '
  for f do
    cp "${f%t.gif}.gif" "$f"
  done' sh {} +

或者与zsh

autoload zmv # if not already in ~/.zshrc
zmv -C '(**/)(*)t.gif(#qD.)' '$1$2.gif'

相关内容