在find的exec参数中扩展bash函数

4
我如何让 find 命令在其 exec 参数中应用我的 shell 定义的函数和别名?
例如,我定义了一个类似于 bzip2 的函数,但使用 7z

function 7zip() { for f in $@; do ls -alF "$f"; 7za a -t7z -m0=lzma -mx=9 -mfb=64 -md=64m -ms=on "$f.7z" "$f" && touch -r "$f" "$f.7z" && rm -fv "$f" && ls -alF "$f.7z"; done; }

当我查找需要压缩的文件时,这个函数会在其中发挥作用:
find . -mtime +7  -name "G*.html"   -execdir  7zip {}  + 

在一个shell脚本中,如果要扩展7zip,会出现“命令未找到”的错误。


总结一些评论:1. 在 ~/.bashrc 中定义和 export -f 7zipi 2. 使用 find ... -exec bash -c "7zipi {}" + - Marcos
最终使用了 find . -mtime +7 -name "G*.html" -execdir bzip2 -9v {} \;,因为在这些较小的文件大小下,它提供了稍微更好的压缩效果,并且 .bz2 是 UNIX 中更容易处理的格式,以便以后解压缩。 - Marcos
4个回答

2

您可以使用以下代码导出函数定义:

export -f 7zipi

但使用以数字开头的标识符是在寻找麻烦。尝试将名称更改为有意义的内容(例如“f7zipi”或“_7zipi”)。


尝试过了,但仍然出现“find: '7zipi': 没有那个文件或目录”的错误。 - Marcos
它对我有效,但它只在该 bash 会话的持续时间内有效... 要使其永久生效,请将函数定义和导出行都添加到您的 ~/.bashrc 文件中。 - Peter.O
奇怪...在Bash v. 3.2.39和find(GNU findutils)v. 4.4.0上对我无效。看起来这是find的一个限制,它不接受函数作为--execdir参数。 - Nik O'Lai
2
@nik,Find 命令无法通过 bash 调用。 请尝试使用 -execdir bash -c 'zipi7 {}' ;。 - William Pursell
@WilliamPursell,谢谢,使用显式的bash确实有效。 - Nik O'Lai
显示剩余2条评论

1
作为一个急躁的程序员,我现在将其改成了多行:
hitlist=$(find . -mtime +7  -name "G*.html")
7zipi $hitlist |awk ' !x[$0]++'

顺便提一下,末尾的awk是为了使输出仅打印之前未见过的行,以免与无数行混杂在一起:

7-Zip (A) 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,2 CPUs)
Compressing  [Content]      
Everything is Ok

但这并不是真正的答案;我仍然希望find通常使用我的宏。


请查看我对William Pursell答案的评论,它需要额外的一步才能正常工作。 - Peter.O
@Peter.O 发布你的 bash -c 答案,这样我就可以标记为解决方案。那个东西已经在我的 .bashrc 中了,所以足够接近了。 - Marcos
我使用的方法不使用 bash -c。 我已经发布了它,这样你就可以将其与你拥有的进行比较。 - Peter.O
@Marcos 或者只需:7zipi $(find . -mtime +7 -name "G*.html") - Marcos

1

这四个命令在函数调用中都可以正常工作。根据需要调整查找规范。它们都适用于文件名中的空格。就我个人而言,我看不出启动另一个bash实例的意义,但我包含了两个调用bash的版本。

IFS=$'\n'; f=($(find /tmp -maxdepth 1 -name "$USER.*")); f7zipi "${f[@]}"

IFS=; find /tmp -maxdepth 1 -name "$USER.*" | while read -r f ;do f7zipi "$f"; done 

IFS=$'\n'; bash -c 'IFS=; f7zipi "$@"' 0 $(find /tmp -maxdepth 1 -name "$USER.*")  

find /tmp -maxdepth 1 -name "$USER.*" -exec bash -c 'IFS=; f7zipi "$@"' 0 {} +;   

以下是我在Ubuntu 10.04中使用的GNU bash 4.1.5设置函数的方法。
顺便提一下,您应该在函数中使用“local f”,这样它就不会与调用脚本的同名变量发生冲突。
这就是我添加到我的~/.bashrc文件中的内容。
function f7zipi() { 
    local f
    for f in $@; do 
        ls -alF "$f"
        7za a -si -t7z -m0=lzma -mx=9 -mfb=64 \
        -md=64m -ms=on "$f.7z" < "$f" && 
            touch -r "$f" "$f.7z" && 
            rm -fv "$f" && 
            ls -alF "$f.7z"
    done
}
export -f f7zipi

当我将上述函数分配到终端的bash命令行中时,从该命令行运行的脚本在调用该函数时会失败... 如果我进一步将export -f f7zipi应用于同一命令行.. 那么脚本就会成功... 但是脚本仅适用于该特定命令行会话。
当将函数和导出包含在~/bashrc中时,脚本每次都可以在任何bash会话中正常工作。
这是测试脚本。
#!/bin/bash
f=/tmp/$USER.abc
g=/tmp/$USER.lmn
rm -fv "$f" "$f".7z
rm -fv "$g" "$g".7z
printf 'abcdefg'>"$f"
printf 'lmnopqr'>"$g"
IFS=$'\n'; f=($(find /tmp -maxdepth 1 -name "$USER.*")); f7zipi "${f[@]}"
exit

你打算演示一下如何在 find-execdir-exec 中使用吗? - Marcos
@Marcos,不是我建议在Nik O'Lai的评论中调用子shell,而是William Pursell... 我只是为了可能导致函数调用错误而介入... - Peter.O
我现在已经添加了4种使用find输出调用函数的方法... 两种方法使用了 find -exec - Peter.O
感谢您进行全面的测试!顺便说一下,我切换回了之前的 7zip 函数,通过 <"$f" 那样从 stdin 进行压缩,可以保留内部文件的时间戳。此外,结果表明,对于1000多个60-400kb的HTML文件,使用 bzip2 -9v 稍微好一些。7z似乎在大型/实心存档方面表现更好,其大小为几MB。 - Marcos

0

似乎并不是每个find都接受函数作为--execdir参数。对我来说,无论是原始形式还是使用export -f都没有起作用。

但是,如果你将函数制作成脚本,它就会起作用。

find . -mtime +7 -name "G*.html" -execdir  /path/to/script_7zipi {} +

真的。我可以坚持使用更便携的 bzip2 -9v,或者希望一些包装脚本最终被打包到UNIX的7z中,使其更像在管道等中使用的传统压缩程序。 - Marcos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接