Bash 使用sed、shell或其他方式进行行内文本替换
我想向程序传递两个参数,一个文件名和一个文件名的修改版本。情况是,我在目录树中有一堆.html.erb文件,我希望在它们上调用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
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