对目录下的所有文件执行命令

有人能提供代码来做以下事情吗? 假设有一个文件目录,所有这些文件都需要通过一个程序运行。程序将结果输出到标准输出。我需要一个脚本,将进入一个目录,在每个文件上执行命令,并将输出连接到一个大输出文件

例如,在1个文件上运行命令:

$ cmd [option] [filename] > results.out
506449 次浏览

这个怎么样:

find /some/directory -maxdepth 1 -type f -exec cmd option {} \; > results.out
  • -maxdepth 1参数阻止find函数递归降入 任何子目录。(如果你想处理这样的嵌套目录,你可以省略这个)
  • -type -f指定只处理普通文件。
  • -exec cmd option {}告诉它为找到的每个文件运行cmd和指定的option,文件名替换为{}
  • \;表示命令的结束。
  • 最后,所有单独cmd执行的输出被重定向到 李results.out < / >

然而,如果您关心文件处理的顺序,您可以使用 也许写个循环会更好。我认为find处理文件 在inode顺序(虽然我可能是错的),这可能不是什么 你想要的。< / p >

下面的bash代码将把$file传递给命令,其中$file将表示/dir中的每个文件

for file in /dir/*
do
cmd [option] "$file" >> results.out
done

例子

el@defiant ~/foo $ touch foo.txt bar.txt baz.txt
el@defiant ~/foo $ for i in *.txt; do echo "hello $i"; done
hello bar.txt
hello baz.txt
hello foo.txt

基于@Jim Lewis的方法:

下面是一个使用find并根据修改日期对文件进行排序的快速解决方案:

$ find  directory/ -maxdepth 1 -type f -print0 | \
xargs -r0 stat -c "%y %n" | \
sort | cut -d' ' -f4- | \
xargs -d "\n" -I{} cmd -op1 {}

排序参见:

http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time

我在树莓派的命令行中运行:

for i in *; do cmd "$i"; done

我需要将所有.md文件从一个目录复制到另一个目录,下面是我所做的。

for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done

这很难读,所以让我们分解一下。

第一张CD放入你的文件目录,

for i in **/*.md;为你的模式中的每个文件

mkdir -p ../docs/"$i"make该目录在docs文件夹外的文件夹包含你的文件。这将创建一个与该文件同名的额外文件夹。

rm -r ../docs/"$i"删除由于mkdir -p而创建的额外文件夹

cp "$i" "../docs/$i"复制实际文件

echo "$i -> ../docs/$i"回声你做了什么

从此过上幸福的生活

一种快速而肮脏的方法有时可以完成工作:

find directory/ | xargs  Command

例如,要查找当前目录中所有文件的行数,您可以这样做:

find . | xargs wc -l

公认的/高投票的答案是很好的,但他们缺乏一些本质的细节。这篇文章涵盖了如何更好地处理当shell路径名展开(glob)失败时,当文件名包含嵌入的换行符/破折号时,以及在将结果写入文件时将命令输出重定向移出for循环的情况。

当使用*运行shell glob扩展时,如果目录中存在没有文件,那么扩展可能会失败,并且将未展开的glob字符串传递给要运行的命令,这可能会产生不希望看到的结果。bash shell使用nullglob提供了一个扩展的shell选项。因此,在包含文件的目录中,循环基本上如下所示

 shopt -s nullglob


for file in ./*; do
cmdToRun [option] -- "$file"
done

当表达式./*不返回任何文件(如果目录为空)时,这可以让你安全退出for循环

或以POSIX兼容的方式(nullglob是特定于bash的)

 for file in ./*; do
[ -f "$file" ] || continue
cmdToRun [option] -- "$file"
done

这让你在表达式失败一次时进入循环,条件[ -f "$file" ]检查未展开的字符串./*是否是该目录中的有效文件名,而不是。因此,在这种情况下失败,使用continue,我们恢复回for循环,该循环随后不会运行。

还要注意在传递文件名参数之前使用--。这是必需的,因为如前所述,shell文件名可以在文件名中的任何位置包含破折号。一些shell命令解释了这一点,当名称被正确引用时,将它们视为命令选项,如果提供了标志,则执行命令思维。

在这种情况下,--标志着命令行选项的结束,这意味着命令不应该将超出这一点的任何字符串解析为命令标志,而只能将其解析为文件名。


文件名的双引号可以正确地解决名称包含glob字符或空格的情况。但是*nix文件名也可以包含换行符。因此,我们用唯一不能成为有效文件名一部分的字符来限制文件名——空字节(\0)。由于bash内部使用C样式的字符串,其中null字节用于表示字符串的结束,因此它是正确的候选对象。

因此,使用shell的printf选项,使用read命令的-d选项,用这个NULL字节分隔文件,我们可以执行以下操作

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done

nullglobprintf被包裹在(..)周围,这意味着它们基本上是在子shell(子shell)中运行的,因为为了避免一旦命令退出,nullglob选项反射到父shell上。read命令的-d ''选项是 POSIX兼容的,因此需要一个bash shell来完成此操作。使用find命令可以这样做

while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)

对于不支持-print0find实现(除了GNU和FreeBSD实现),可以使用printf来模拟

find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --

另一个重要的修复是将重定向移出for循环,以减少大量的文件I/O。当在循环内部使用时,shell必须为for循环的每次迭代执行两次系统调用,一次用于打开与文件相关的文件描述符,一次用于关闭。这将成为运行大型迭代时性能的瓶颈。建议将其移到循环之外。

您可以使用此修复扩展上面的代码

( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done > results.out

这将基本上将您的文件输入的每次迭代的命令内容放到标准输出中,当循环结束时,打开目标文件一次以写入标准输出的内容并保存它。等价的find版本的same将是

while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out

我认为简单的解决方法是:

sh /dir/* > ./result.txt

Maxdepth

我发现它在Jim Lewis的回答 .中工作得很好,只需要像这样添加一点:

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
$ find . -maxdepth 1 -type f -name '*.sh' -exec {} \; > results.out

排序顺序

如果你想按排序顺序执行,可以这样修改:

$ export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -maxdepth 2 -type f -name '*.sh' | sort | bash > results.out

举个例子,它将按照以下顺序执行:

bash: 1: ./assets/main.sh
bash: 2: ./builder/clean.sh
bash: 3: ./builder/concept/compose.sh
bash: 4: ./builder/concept/market.sh
bash: 5: ./builder/concept/services.sh
bash: 6: ./builder/curl.sh
bash: 7: ./builder/identity.sh
bash: 8: ./concept/compose.sh
bash: 9: ./concept/market.sh
bash: 10: ./concept/services.sh
bash: 11: ./product/compose.sh
bash: 12: ./product/market.sh
bash: 13: ./product/services.sh
bash: 14: ./xferlog.sh

无限的深度

如果你想在特定条件下无限深度执行,你可以使用这个:

export DIR=/path/dir && cd $DIR && chmod -R +x *
find . -type f -name '*.sh' | sort | bash > results.out

然后把子目录中的每个文件放在上面,就像这样:

#!/bin/bash
[[ "$(dirname `pwd`)" == $DIR ]] && echo "Executing `realpath $0`.." || return

在父文件主体的某个地方:

if <a condition is matched>
then
#execute child files
export DIR=`pwd`
fi

你可以使用xarg:

ls | xargs -L 1 -d '\n' your-desired-command
  • -L 1导致每次传递一个项

  • -d '\n'根据换行拆分ls的输出。