随着时间的推移,我一次又一次遇到相同的模式:我有某种目录结构:
example/
├── a
│ └── c
│ ├── d.txt (120k)
│ └── e.txt (60k)
└── b
└── f.txt (280k)
我想将文件“复制”到另一个目录,例如,example_grepped
对每个文件应用一个命令,就像代替cp
- 比如说,grep ERROR
这样说,我最终得到一个具有相同结构的文件夹,但文件通过grep
。
example_grepped/
├── a
│ └── c
│ ├── d.txt (1k)
│ └── e.txt (0b)
└── b
└── f.txt (12k)
转换媒体文件(FLAC 到 MP3、PNG 到 JPG)的模式相同,这次在构建过程中转换不同的架构格式。
有我可以使用的通用命令吗?类似foobar example example_grepped --command 'grep ERROR'
或foobar flacs mp3s --command 'ffmpeg -i {} {}.mp3'
?
也许是一面不起眼的xargs
旗帜? (find
通过管道xargs
将几乎足够了,但大多数(如果不是全部)命令都期望目录结构已经存在。)
答案1
我可以找到的最接近的答案,无需单独重新创建目录结构,就是使用安装:
cd example
find . -type f -exec sh -c 'grep ERROR {} | install -D /dev/stdin /tmp/example_grepped/{}' \;
不幸的是,只有当您的命令可以将其结果抛出到 STDOUT 时,上述内容才有效。
答案2
解决此问题的另一种方法是使用无论如何都会进行递归复制的程序。我查了一下rsync
,但快速浏览后找不到回调选项。但是 gnutar
有一个选项--to-command
,您可以提供一个要运行的命令,将文件的输入获取到stdin
.但是如何创建文件呢?好吧,调用的命令会查找 中的当前文件名$TAR_FILENAME
。
把它们放在一起,基本的调用是
tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"
其中脚本可能类似于
#!/bin/bash
mkdir -p $(dirname "$1/$TAR_FILENAME")
grep '$2' >"$1/$TAR_FILENAME"
exit 0
解决此问题的另一种方法是将 tar 管道包装在脚本中,该脚本使命令在命令行上运行。然而,该构造的转义mkdir ...dirname
将有点具有挑战性。
答案3
#!/bin/bash
filter() {
local target_root="${@: -1}"
target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
target_dir=$(dirname "$target_path")
mkdir -p "$target_dir"
if [[ -f $1 ]]; then
# do your grep thing here
grep burger "$1" > "$target_path"
fi
}
export -f filter
source_root="example"
target_root="example_grepped"
find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"
此脚本还适用于包含空格的目录和文件名。
在源目录(“示例”)所在的位置运行此脚本。
答案4
使用 GNU Parallel 你可以做这样的事情:
cd src
find . -type f | parallel 'mkdir -p ../dst/{//}; dostuff --input {} --output ../dst/{}'