Bash 使用sed、shell或其他方式进行行内文本替换

Bash 使用sed、shell或其他方式进行行内文本替换,bash,shell,sed,find,haml,Bash,Shell,Sed,Find,Haml,我想向程序传递两个参数,一个文件名和一个文件名的修改版本。情况是,我在目录树中有一堆.html.erb文件,我希望在它们上调用html2haml,使用原始文件名和带有haml扩展名的新输出文件名,如下所示: html2haml thing.html.erb thing.html.haml 以下是我目前对此的最佳尝试: find . -name "*.html.erb" -exec echo {} `echo {} | sed "s/.erb/.haml/g"` \; (完成测试后,我将用ht

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

html2haml thing.html.erb thing.html.haml
以下是我目前对此的最佳尝试:

find . -name "*.html.erb" -exec echo {} `echo {} | sed "s/.erb/.haml/g"` \;
(完成测试后,我将用
html2haml
替换
echo
,然后再次运行它)

但是它不起作用。backticks中表达式的结果是未修改的字符串

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

事实上,所有这些行为都如预期的那样,这向我表明,我在语法上有一些小错误,而使用一行程序确实可以做到这一点。


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

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

do_something "$filename" "${filename%.foo}.foo2"
(在不太可能的情况下,您真的想在末尾加一个
2
,当然可以使用
“${filename}2”
。但是我假设
foo
foo2
将被类似的字符串替换。)

如果您想从
find
调用
dou\u 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 {} +
  bar "$filename" "$(sed -r 's/([^.]+)\.([^.]+)$/\2.\1/' <<<"$filename")"
如果您确实需要使用
sed
(对于您甚至无法使用
bash
replace语法的部分,
${var/pattern/substitution}
),则如上所述设置
dou something
,但将
for
循环中的行替换为,例如:

-- 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 {} +
  bar "$filename" "$(sed -r 's/([^.]+)\.([^.]+)$/\2.\1/' <<<"$filename")"

bar“$filename”“$(sed-r的/([^.]+)\([^.]+)$/\2.\1/”您可以这样调用您的查找:

find . -name "*.html.erb" -print0 -print0|xargs -0 -J % html2haml % | sed 's/\.erb$/.haml/'
这将导致执行:

html2haml thing.html.erb thing.html.haml

如果您有
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

您有一个GNU sed

以上内容-对于下一个
查找

$ find . -name \*foo -print 
./a/test.foo
./b/c/test.foo
./b/te st.foo         #<- note the filename with space
./b/test.foo
没有额外的脚本或函数。;)

或者您可以将
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/')"' \;
或者省略两个无用的回显最终命令:

find . -name \*html.erb -exec sh -c 'html2haml "{}" "$(sed 's/\.erb/\.haml/'<<<"{}")"' \;
find.-name\*html.erb-exec sh-c'html2haml“{}”“$(sed's/\.erb/\.haml/”循环怎么样

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

${var%glob}
语法使用环境变量
${var}
并过滤掉与
glob

匹配的右侧最小部分。但是正如您所怀疑的,它不适用于我的sed。(OSX上的bsd):-)我正在寻找一个尽可能通用的解决方案,这样就可以把它放在一些软件的文档中。有没有办法用一个更常用的工具查找/替换,并将其保持为一行程序?(或者,更通用的sed语法?)我已经澄清并扩展了我的问题——看看它。它是有效的!sed语法是否适用于您的gsed?这是我的最后一个调用:
find-名称\*.erb-打印| sed'p;s/.erb$/.haml/'| xargs-n2 html2haml
@JohnBachir添加了
一个查找
示例too@JohnBachir您需要使用shell,因为您正在使用管道和命令Sobitutions以及soo-所有这些都是shell特性。如果没有shell,它什么都不做。我想做的是更改树中某些文件的文件扩展名。我已经修改了我的问题,以便它描述了我的实际用例,希望它更清楚。我希望它是一行。我澄清并扩展了我的问题,请查看。对于任何与Bourne兼容的shell,您可以直接从命令行输入此脚本。我一直在Kornshell和BASH中这样做。我希望它是一行-我已经澄清并扩展了我的问题,看一看。这个问题的答案已经进入了这个pull请求中::-d
find . -name "*.html.erb" | while read file
do
    haml_file=${file%.erb}.haml
    html2haml $file $haml_file
done