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


1

我编写了一些脚本foo,该脚本接受文件路径,读取文件,对其进行一些更改,然后将更改后的文件输出到stdout:

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是使事情变得复杂的方法。

Answers:


2

如果使用递归遍历,则打字和摆弄的次数要少得多。在bash,把shopt -s globstar你的.bashrc。在ksh93中,递归glob需要set -o globstar; 在zsh中,它可以直接使用。请注意,在bash中,递归glob也会在指向目录的符号链接下递归。

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

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 $< >$@

1

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

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

FWIW,您只需要一个扩展#%在这些扩展中,因为没有通配符,因此您要删除“最小”的部分。例如${sf#./src/}
Stephen Harris

@StephenHarris FWIW,如果./src/.foo是固定的,##%%具有与完全相同的效果#%。:)
佐藤桂

是的,他们做同样的事情,但不必要的额外打字;-)
史蒂芬·哈里斯

1
它真的比原始解决方案简单吗?我的意思是,如果我要我的同事(前端开发人员)在终端机中输入密码,他们将把椅子扔给我,然后故意将咖啡倒在我的键盘上。
奥列格

1
@Oleg然后,我认为对于临时用户而言,最简单的方法是拥有一个包装脚本,该包装脚本接受srcdest作为参数,并应用于foo所有.foo文件。否则,我认为这不是一个具有简单安全的解决方案的问题(无论如何,它并不比上述方法简单得多)。
桂聪(SatōKatsura)

1

假设您没有一棵源文件树(例如,否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

如果源目录.foo的子目录中有文件,则此方法不起作用。
聪桂

对; 我假设没有子目录,因为那时我们还需要确保dest也创建了子目录;不能仅>dest/$name$name可能具有目录组件时执行操作。
史蒂芬·哈里斯

我确实有嵌套的src目录结构,在问题中忘了提及它,但这可能不是问题。我只是想知道:for循环是否适用于所有* nix口味,还是有些陷阱?(对不起,我是菜鸟。。。我真的应该读更多关于这些东西的信息)
Oleg

for循环是标准的sh。该${../../..}语法是一个bashksh; dunno(如果不是POSIX)。
史蒂芬·哈里斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.