【问题标题】:In-line text replacement using sed, shell, or some other means使用 sed、shell 或其他方式进行内联文本替换
【发布时间】:2013-07-08 17:55:59
【问题描述】:

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

html2haml thing.html.erb thing.html.haml

这是我目前最好的尝试:

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

(在我完成测试后,我会将echo 替换为html2haml 并再次运行)

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

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

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”连接起来)。

【问题讨论】:

标签: bash shell sed find haml


【解决方案1】:

如果你有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 sed 被称为 gsed,如果你的 sed 是 GNU - 而不是 gsed 使用 sed

您可以使用sed --version 检查您的sed,如果打印出如下内容:

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

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

或者你可以将sed替换为perl,所以接下来

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/'<<<"{}")"' \;

【讨论】:

  • 可爱。但正如您所怀疑的,不适用于我的 sed。 (bsd on os x) :-) 我正在寻找尽可能通用的解决方案,以便可以将其放入某些软件的文档中。有什么想法可以用更常见的工具进行查找/替换,并保持单线? (或者,更通用的 sed 语法?)
  • 我已经澄清并扩展了我的问题——检查一下。
  • 它有效! sed 语法是否适用于您的 gsed?这是我的最后一次调用:find . -name \*.erb -print | sed 'p;s/.erb$/.haml/' | xargs -n2 html2haml
  • @JohnBachir 也添加了one find 示例
  • @JohnBachir 你需要执行 shell,因为你在做管道和命令 sobtituations 和 soo - 都是 shell 功能。没有外壳它什么都不做
【解决方案2】:

你可以这样调用你的 find:

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

这将导致执行:

html2haml thing.html.erb thing.html.haml

【讨论】:

  • 我希望它是单行的——我已经澄清并扩展了我的问题,看看。
【解决方案3】:

循环呢?

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

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

【讨论】:

  • 我希望它是单行的。我澄清并扩展了我的问题,请查看。
  • 使用任何与 Bourne 兼容的 shell,您都可以直接从命令行输入此脚本。我一直在 Kornshell 和 BASH 中这样做。
【解决方案4】:

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

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

(在不太可能的情况下,您真的只想在末尾加上2,当然可以只使用"${filename}2"。但我假设foofoo2 将被替换为less类似的字符串。)

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

-- 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 使用扩展的正则表达式格式,我发现它更具可读性。 &lt;&lt;&lt; 是一种 bashism,它扩展了它后面的单词并将其输入 stdin,有点类似于 echo "$filename" | sed ...,但没有创建另一个子进程。

【讨论】:

  • 我想要做的是更改树中某些文件的文件扩展名。我修改了我的问题,以便描述我的实际用例,希望它更清楚。
猜你喜欢
  • 2017-08-23
  • 2020-02-09
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 2018-11-16
  • 1970-01-01
  • 2019-11-28
  • 2012-02-10
相关资源
最近更新 更多