查找文件、应用更改并以不同名称写入另一个目录的简单方法?

查找文件、应用更改并以不同名称写入另一个目录的简单方法?

我编写了一些脚本foo,它接受文件路径,读取文件,对其应用一些更改,并将更改后的文件输出到标准输出:

foo src/file.foo > dest/file.changed.foo # works fine
cat src/file.foo | foo > dest/file.changed.foo # also works fine

现在,我想将此命令应用于目录中的多个文件,并对每个文件执行以下操作:

src/file.foo --> foo --> dest/file.changed.foo

基于回答到一个类似的问题我想出了以下几点:

find src -name '*.foo' \
  -exec bash -c 'for x; do dest=${x/src/dest}; foo ${x} > ${dest/\.foo$/.changed.foo}; done' _ {} +

上面的方法可行,但问题是对于日常使用来说太复杂了。难道就没有更简单的方法吗?我想知道是否是foo这样做的方式让事情变得复杂了。

答案1

如果您使用递归通配符,那么打字和摆弄就会少得多。在bash中,输入shopt -s globstar在你的.bashrc。在 ksh93 中,递归通配符需要set -o globstar;在 zsh 中,它开箱即用。请注意,在 bash 中,递归通配符也会在目录的符号链接下递归。

要节省字符串操作,请首先更改到源树的顶部(或目标树的顶部)。

cd foo
for x in **/*.foo; do foo "$x" >"../dest/${x%.*}.changed.foo"; done

如果您知道文件名不包含空格或通配符,则可以省略双引号。

在 zsh 中,双引号是不必要的,即使不更改当前目录,您也可以节省更多的输入。

for x in src/**/*.foo; do foo $x >../dest/${${x#*/}%.*}.changed.foo; done
for x in src/**/*.foo; do foo $x >../dest/${x#*/}:r.changed.foo; done
for x (src/**/*.foo) foo $x >../dest/${x#*/}:r.changed.foo

如果你经常这样做,你应该定义一个构建规则,例如在 GNUmakefile 中(使用一个制表符,其中我使用了 8 个空格):

source_files = $(shell find src -name '*.foo')
destination_files = $(patsubst src/,dest/,$($(source_files)%.foo=.changed.foo))

default: all-foo
all-foo: $(destination_files)

dest/%.changed.foo: src/%.foo
        foo $< >$@

答案2

假设您的文件名没有嵌入换行符:

find src -name '*.foo' -type f | \
    while IFS= read -r sf; do
        df=dest/${sf##./src/} && \
        mkdir -p "$(dirname "$df")" && \
        foo "$sf" >"${df%%.foo}.changed.foo"
    done

答案3

假设您没有源文件树(例如 no src/dir1/a.foo;它们都直接在src目录中),那么您不需要find包装器,只需直接在 shell 中执行即可:

for x in src/*.foo
do
  dest="${x/src/dest}"
  foo "$x" > "${dest/%.foo/.changed.foo}"
done

这里的复杂之处在于您想要更改文件名,并且这不是foo.

foo如果不需要在当前目录中运行,我们可以让它稍微容易一些

cd src
for x in *.foo
do
  foo "$x" > "../dest/${x/%.foo/.changed.foo}"
done

如果您不想更改扩展名,那么这就是最简单的循环

cd src
for x in *.foo
do
  foo "$x" > "../dest/$x"
done

通过策略性地使用角色,所有这些循环都可以变成单行;。例如第一个循环变成

for x in src/*.foo; do dest="${x/src/dest}" ; foo "$x" > "${dest/%.foo/.changed.foo}" ; done

相关内容