4

我想将两个参数传递给程序,一个文件名和文件名的修改版本。情况是我在目录树中有一堆 .html.erb 文件,我想html2haml用原始文件名和带有 haml 扩展名的新输出文件名调用它们,如下所示:

html2haml thing.html.erb thing.html.haml

这是我目前最好的尝试:

find . -name "*.html.erb" -exec echo {} `echo {} | sed "s/.erb/.haml/g"` \;

(在我完成测试后,我将替换echohtml2haml并再次运行它)

但是它不起作用。反引号内的表达式的结果是未修改的字符串。

以下是我尝试的一些实验,它们的行为符合预期(测试我的语法和转义/引号的级别是否正确):

1. echo myfile.foo | sed 's/foo/foo2/g'
2. find . -name "*.html.erb" -exec echo {} `echo xyz | sed "s/y/Y/g"` \;
3. find . -name "*.html.erb" -exec echo {} `echo {} hello` \;
4. find . -name "*.html.erb" -exec echo {} `echo {}` \;

这些都按预期运行的事实向我表明,我在语法中遇到了一些小错误,而这确实可以用单行来做到这一点。

如果这是不可能的,那可能是因为对“何时”find 在每次调用时插入其结果的误解。上面的示例#3 向我建议它在我需要/期望它时执行它(因为我成功地将每个单独的结果字符串与“hello”连接起来)。

4

4 回答 4

4

如果您有gsed

find . -name \*.erb -print0 | gsed -z 'p;s/.erb$/.haml/' | xargs -0 -n2 html2haml

如果您没有gsed并且只有sed,这将起作用,但前提是您的文件名都没有空格

find . -name \*.erb -print | sed 'p;s/.erb$/.haml/' | xargs -n2 html2haml

关于这些和其他技术的讨论如下:

我有不同版本的 sed -如果您是 GNU ,GNU sed则称为我的 - 而不是使用。gsedsedgsedsed

你可以sedsed --version, 如果打印出类似的东西来检查你的:

sed (GNU sed) 4.2.2
Copyright (C) 2012 Free Software Foundation, Inc.

你有一个 GNU sed。

以上 - 为下一个find

$ find . -name \*foo -print 
./a/test.foo
./b/c/test.foo
./b/te st.foo         #<- note the filename with space
./b/test.foo

上面的命令产生:

$find . -name \*foo -print0 | gsed -z 'p;s/foo$/foo2/' | xargs -0 -n2 echo bar
bar ./a/test.foo ./a/test.foo2
bar ./b/c/test.foo ./b/c/test.foo2
bar ./b/te st.foo ./b/te st.foo2
bar ./b/test.foo ./b/test.foo2

无需额外的脚本或功能。;)

或者您可以将 替换为sedperl所以下一个

find . -name \*foo -print0 | perl  -n0le 'print;s/foo/foo2/;print' | xargs -0 -n2 echo bar

产生相同的结果:

bar ./a/test.foo ./a/test.foo2
bar ./b/c/test.foo ./b/c/test.foo2
bar ./b/te st.foo ./b/te st.foo2
bar ./b/test.foo ./b/test.foo2

如果您真的想在一次查找中做到这一点,请尝试:

find . -name \*html.erb -exec sh -c 'echo html2haml "{}" "$(echo "{}" | sed 's/\.erb/\.haml/')"' \;

或限制两个无用的 echo 最终命令:

find . -name \*html.erb -exec sh -c 'html2haml "{}" "$(sed 's/\.erb/\.haml/'<<<"{}")"' \;
于 2013-07-10T17:35:49.340 回答
1

循环呢?

find . -name "*.html.erb" | while read file
do
    haml_file=${file%.erb}.haml
    html2haml $file $haml_file
done

${var%glob}语法采用环境变量${var}并过滤掉右侧匹配的最小部分glob

于 2013-07-10T18:54:52.343 回答
0

你可以这样称呼你的发现:

find . -name "*.html.erb" -print0 -print0|xargs -0 -J % html2haml % | sed 's/\.erb$/.haml/'

这将导致执行:

html2haml thing.html.erb thing.html.haml
于 2013-07-10T17:34:20.717 回答
0

如果您知道文件名以 结尾.foo,那么您可以使用:

do_something "$filename" "${filename%.foo}.foo2"

(在不太可能的情况下,您真的只想将 a2放在最后,您当然可以只使用"${filename}2"。但我假设fooandfoo2将被替换为不太相似的字符串。)

如果你想调用do_somethingfrom find,你最好的办法是只传递一个文件名(或者更好的是,多个文件名,每个文件名代表一个操作)。例如:

-- do_something.sh

#!/bin/bash

# This is the definition of what you want to do.
# It is called as `bar old_filename new_filename`
bar() {
  # For example
  mv "$1" "$2"
}

for filename in "$@"; do
  bar "$filename" "${filename%.foo}.foo2"
done


-- find command:
find . -type f -name '*.foo' -exec do_something.sh {} +

如果你真的需要使用sed(对于一些你甚至不能用bash替换语法做的事情${var/pattern/substitution}),然后do_something像上面一样设置,但是将for循环内的行替换为,例如:

  bar "$filename" "$(sed -r 's/([^.]+)\.([^.]+)$/\2.\1/' <<<"$filename")"

解释:上面的sed表达式(gnu-specific)翻转了最后两个扩展,所以它会some.file.html.en变成some.file.en.html. -r导致 gnu sed 使用扩展的正则表达式格式,我发现它更具可读性。<<<是一种 bashism,它扩展了它后面的单词并将其输入stdin,有点类似于echo "$filename" | sed ...但没有创建另一个子过程。

于 2013-07-10T17:22:54.887 回答