使用多个命令查找-exec


429

我试图将find -exec与多个命令一起使用而没有成功。有人知道以下命令是否可行吗?

find *.txt -exec echo "$(tail -1 '{}'),$(ls '{}')" \;

基本上,我正在尝试在当前目录中打印每个txt文件的最后一行,并在该行的末尾打印,逗号后跟文件名。



1
至于检查命令的可能性,您是否在系统上尝试过该命令?
斯里拉姆

1
find手册页:There are unavoidable security problems surrounding use of the -exec option; you should use the -execdir option instead.unixhelp.ed.ac.uk/CGI/man-cgi?find
JVE999,2014年


@ JVE999链接已损坏,替代方法为ss64.com/bash/find.html
Keith M,

Answers:


657

find接受-exec命令的多个部分。例如:

find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;

请注意,在这种情况下,只有第一个命令成功返回时,第二条命令才会运行,如@Caleb所述。如果要使两个命令无论成功与否都运行,则可以使用以下结构:

find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;

如何两次grep?这是失败的:查找./* -exec grep -v'COLD,'{} \; -exec egrep -i“ my_string” {} \;
rajeev

51
@rajeev仅当第一个执行程序的返回代码返回成功时,第二个执行程序才会运行,否则将被跳过。此答案中可能应注意这一点。
Caleb 2014年

1
这就是答案
pylover

1
请注意-n,在其他一些答案中使用来抑制echo生成的换行符,如果第二个命令仅产生一行输出并且希望它们更易于阅读,这将很方便。
William Turrell '18

很好的解决方案。奇迹般有效。
菲利普·德尔特伊


52

以下之一:

find *.txt -exec awk 'END {print $0 "," FILENAME}' {} \;

find *.txt -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;

find *.txt -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;

12
{}之前的下划线是什么?
QED

2
@qed:它是一个抛弃式的值,它占据的位置$0。尝试使用“ foobar”而不是“ _”:find /usr/bin -name find -exec sh -c 'echo "[$0] [$1]"' foobar {} \;-输出:“ [foobar] [/ usr / bin / find]”。
暂停,直到另行通知。

1
@XuWang:是的,我想是的。如您所知,$0通常是程序名称(ARGV[0])。
暂停,直到另行通知。

3
对于此方法,至关重要的是,传递给的脚本必须sh -c使用单引号而不是双引号。否则$1将在错误的范围内。
尼克

1
@Nick引号与它无关- '$1'只要您对变量("\$1")进行转义,就可以用双引号引起来。您也可以转义其他字符("\"")。
卡米洛·马丁

22

另一种方式是这样的:

multiple_cmd() { 
    tail -n1 $1; 
    ls $1 
}; 
export -f multiple_cmd; 
find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;

在一排

multiple_cmd() { tail -1 $1; ls $1 }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
  • multiple_cmd()”-是一个功能
  • export -f multiple_cmd”-将导出它,以便任何其他子外壳都可以看到它
  • find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;”-找到将在您的示例上执行功能的代码

这样,multiple_cmd可以根据需要长而复杂。

希望这可以帮助。


完美,正是我所需要的!
Anentropic

在OSX上对我不起作用
Thomas

@Thomas可以,但是可以尝试这种在OSX中测试过的衬垫。我创建了一个名为“ aaa”的目录,其中包含一些文件/目录,并将其CDd到其中。然后, ~/aaa$ acmd() { echo x \"$1\" x; }; export -f acmd; find . -exec bash -c 'acmd {}' \;
barlop

@barlop,我会尝试一下;谢谢!
托马斯

14

有一种更简单的方法:

find ... | while read -r file; do
    echo "look at my $file, my $file is amazing";
done

或者:

while read -r file; do
    echo "look at my $file, my $file is amazing";
done <<< "$(find ...)"

1
文件名中可以​​包含换行符,这就是为什么find具有-print0参数并且xargs具有-0参数的原因
abasterfield

@abasterfield我总是希望永远不要在野外找到那些人
Camilo Martin

1
我想做的是“找到... -exec zcat {} | wc -l \;” 这没有用。但是,找到... | 在读取-r文件时;做echo“ $ file:zcat $file | wc -l”; 完成的工作,所以谢谢!
Greg Dougherty

在上面的注释中,我在“ zcat $ file | wc -l”周围有“后退标记”。不幸的是,SO会将其转换为格式,因此我将其添加为具有正确可见代码的实际答案
Greg Dougherty

1
@GregDougherty您可以`使用反斜杠来避免使用反斜杠,例如:(\​`仍然,这是使用反斜杠的另一个很好的理由$())。
卡米洛·马丁

8

我不知道您是否可以使用find来做到这一点,但是另一种解决方案是创建一个shell脚本并使用find运行它。

lastline.sh:

echo $(tail -1 $1),$1

使脚本可执行

chmod +x lastline.sh

用途find

find . -name "*.txt" -exec ./lastline.sh {} \;

7
反引号已弃用,请鼓励使用$(...),它可读性更好,独立于字体且易于嵌套。谢谢。
用户未知

4

Denis的第一个答案是解决问题的答案。但实际上,不再像标题建议那样仅在一个exec中包含多个命令。要用几个命令回答一位执行官,我们将不得不寻找其他解决方法。这是一个例子:

保留最近10000行的.log文件,这些行在最近7天内已使用1个exec命令(使用多个{}引用)进行了修改

1)查看命令将对哪些文件执行的操作:

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "echo tail -10000 {} \> fictmp; echo cat fictmp \> {} " \;

2)这样做:(请注意,不再需要“ \>”,而只需要“>”)

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "tail -10000 {} > fictmp; cat fictmp > {} ; rm fictmp" \;


但是,我相信,如果其中一个文件名具有空格,这将中断。
卡米洛·马丁

4

感谢卡米洛·马丁(Camilo Martin),我能够回答一个相关的问题:

我想做的是

find ... -exec zcat {} | wc -l \;

这没有用。然而,

find ... | while read -r file; do echo "$file: `zcat $file | wc -l`"; done

确实有效,所以谢谢!


2

扩展@Tinker的答案,

就我而言,我需要command | command | command在内-exec进行打印以在包含特定文本的文件中打印文件名和找到的文本。

我能够做到:

find . -name config -type f \( -exec  grep "bitbucket" {} \; -a -exec echo {} \;  \) 

结果是:

    url = git@bitbucket.org:a/a.git
./a/.git/config
    url = git@bitbucket.org:b/b.git
./b/.git/config
    url = git@bitbucket.org:c/c.git
./c/.git/config

1
您还可以通过将/dev/null第二个参数作为参数传递给grep带有一个-exec参数的命令,从而在一行上打印文件名和grep内容:find . -name config -type f -exec grep "bitbucket" {} /dev/null \;
Bill Feth

1

应该使用xargs :)

find *.txt -type f -exec tail -1 {} \; | xargs -ICONSTANT echo $(pwd),CONSTANT

另一个(在osx上工作)

find *.txt -type f -exec echo ,$(PWD) {} + -exec tail -1 {} + | tr ' ' '/'

3
这忽略了一个主要用例find-命令行中匹配文件的数量太大的情况。-exec是绕过此限制的一种方法。向公用事业提供管道无法获得这种好处。
克里斯·约翰逊

xargs -n存在以选择每次调用的匹配数。xargs -n 1 foocmd将为foocmd {}每场比赛执行。
AndrewF

0

一个find+xargs答案。

下面的示例查找所有.html文件,并创建一个带有.BAK扩展名的副本(例如1.html> 1.html.BAK)。

具有多个占位符的单个命令

find . -iname "*.html" -print0 | xargs -0 -I {} cp -- "{}" "{}.BAK"

具有多个占位符的多个命令

find . -iname "*.html" -print0 | xargs -0 -I {} echo "cp -- {} {}.BAK ; echo {} >> /tmp/log.txt" | sh

# if you need to do anything bash-specific then pipe to bash instead of sh

此命令也可用于以连字符开头或包含空格的文件,例如,-my file.html由于参数引号引起的,--之后cp则向cp参数末尾和实际文件名的开头发出信号。

-print0 用空字节终止符传递结果。


对于xargs,-I {}参数定义{}为占位符;您可以使用任何您喜欢的占位符;-0指示输入项以空分隔。

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.