复制目录结构,将命令应用于每个文件而不是简单地复制它?

复制目录结构,将命令应用于每个文件而不是简单地复制它?

随着时间的推移,我一次又一次遇到相同的模式:我有某种目录结构:

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/{}'

相关内容