提问人:themaestro 提问时间:5/10/2012 最后编辑:codeforesterthemaestro 更新时间:7/20/2022 访问量:555201
对目录中的所有文件执行命令
Execute command on all files in a directory
问:
有人可以提供代码来执行以下操作: 假设有一个文件目录,所有这些文件都需要通过程序运行。程序将结果输出为标准输出。我需要一个脚本,该脚本将进入一个目录,对每个文件执行命令,并将输出压缩到一个大的输出文件中。
例如,要对 1 个文件运行命令:
$ cmd [option] [filename] > results.out
答:
这个怎么样:
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
但是,如果您关心文件的处理顺序,则可以
写一个循环可能会更好。我认为处理文件
按 inode 顺序(尽管我可能错了),这可能不是什么
你想要。find
评论
stat
sort
-exec
find
始终是最佳选项,因为您可以使用选项按文件名模式进行过滤,并且可以在单个命令中完成。-name
-exec
find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;
以下 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
评论
/dir/
$file
shopt -s nullglob
shopt -u nullglob #revert nullglob back to it's normal default state
done >results.out
xargs
根据 @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
评论
-print0
find
-0
xargs
-print0
sort
我正在从命令行在我的 Raspberry Pi 上执行此操作,方法是运行:
for i in *; do cmd "$i"; done
评论
$i
ppp -i raw.txt -o processed.txt
for i in *; do ppp -i "$i" -o "$i changed"; done
ppp
for i in ./*
*
我需要将所有 .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"
将该目录设在包含文件的文件夹之外的 docs 文件夹中。这将创建一个与该文件同名的额外文件夹。
rm -r ../docs/"$i"
删除由于以下原因而创建的额外文件夹mkdir -p
cp "$i" "../docs/$i"
复制实际文件
echo "$i -> ../docs/$i"
回声你做了什么
; done
从此过上幸福的生活
评论
**
globstar
shopt -s globstar
有时完成工作的一种快速而肮脏的方法是:
find directory/ | xargs Command
例如,要查找当前目录中所有文件中的行数,可以执行以下操作:
find . | xargs wc -l
评论
~/.local/share/steam
被接受/高票的答案很棒,但它们缺少一些细节。这篇文章介绍了如何更好地处理 shell 路径名扩展 (glob) 失败、文件名包含嵌入的换行符/破折号以及在将结果写入文件时将命令输出重定向移出 for 循环的情况。
使用 运行 shell glob 扩展时,如果目录中没有文件,并且未扩展的 glob 字符串将传递给要运行的命令,则扩展可能会失败,这可能会产生不良结果。shell 为此提供了一个扩展的 shell 选项,使用 。因此,在包含文件的目录中,循环基本上如下所示*
bash
nullglob
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
还要注意 just 在传递文件名参数之前的用法。这是必需的,因为如前所述,shell 文件名可以包含文件名中任何位置的破折号。一些 shell 命令会解释这一点,并在名称未正确引用时将它们视为命令选项,并执行命令,认为是否提供了标志。--
在这种情况下,该信号表示命令行选项的结束,这意味着该命令不应将超出此点的任何字符串解析为命令标志,而只能解析为文件名。--
双引号文件名可以正确解决名称包含全形字符或空格的情况。但是 *nix 文件名也可以包含换行符。因此,我们使用唯一不能成为有效文件名一部分的字符 - 空字节 () 来限制文件名。由于内部使用样式字符串,其中 null 字节用于指示字符串的末尾,因此它是正确的候选者。\0
bash
C
因此,使用 shell 选项使用命令选项用这个 NULL 字节分隔文件,我们可以在下面执行以下操作printf
-d
read
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done
和 the 被包裹起来,这意味着它们基本上在子 shell(子 shell)中运行,因为一旦命令退出,为了避免在父 shell 上反射的选项。命令选项不符合 POSIX,因此需要一个 shell 来完成此操作。使用命令,这可以按以下方式完成nullglob
printf
(..)
nullglob
-d ''
read
bash
find
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)
对于不支持的实现(除了 GNU 和 FreeBSD 实现),可以使用以下方法进行模拟find
-print0
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
这基本上会将文件输入的每次迭代的命令内容放入 stdout,当循环结束时,打开目标文件一次以写入 stdout 的内容并保存它。相同的等效版本将是find
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out
评论
我认为简单的解决方案是:
sh /dir/* > ./result.txt
评论
最大深度
我发现它与 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
原因一次传递 1 个项目-d '\n'
拆分 基于换行的输出。ls
评论
-P 8
-d
brew install findutils
gxargs
xargs
评论
ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out
ls
来驱动 .如果写得很称职,也许你可以简单地做.xargs
cmd
cmd <wildcard>