Embed text in text using sed, shell, or some other means

I want to pass two parameters to the program, the file name and the modified version of the file name. The situation is that I have a bunch of .html.erb files in the directory tree, and I want to call html2haml on them with the original file name and a new output file name with the extension haml, for example:

 html2haml thing.html.erb thing.html.haml 

Here is my best attempt:

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

(after I finished testing, I replaced echo with html2haml and ran it again)

However, this will not work. The result of an expression inside backticks is an immutable string.

Here are some experiments I tried: DO behaves as expected (to check if my syntax and escaping / quotation marks are correct):

 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 {}` \; 

The fact that they all behave as expected tells me that I am a little mistaken in the syntax, and that it is really possible to do this using a single-line interface.

If this is not possible, it may be due to a lack of understanding about the β€œwhen” to find inserts its results for each call. Example # 3 above tells me that he does it exactly when I need / expect it (because I successfully concatenate every single line of the result with "hello").

+4
source share
4 answers

If you have gsed :

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

If you don't have gsed and only sed , this will work , but only if none of your file names have spaces .

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

Discussion of these and other methods:

I have different versions of sed - my GNU sed is called gsed , if your sed is GNU - use sed instead of gsed .

You can check your sed for sed --version if it prints something like:

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

You have GNU sed.

The above is for the next find

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

the above command produces:

 $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 

No additional scripts or functions .;)

or you can replace sed with perl , so the following

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

gives the same result:

 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 

IF YOU REALLY want to do this at a time, try:

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

or eliminating two useless echoes in the last command:

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

How about a loop?

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

The syntax ${var%glob} takes the environment variable ${var} and filters out the smallest part of the right-hand side that matches glob .

+1
source

If you know the file name ends with .foo , you can use:

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

(In the unlikely event that you really want to just put 2 at the end, you could just use "${filename}2" . But I assume that foo and foo2 should be replaced with smaller similar strings.)

If you want to call do_something from find , the best option would be to give it only one file name (or, better, the number of file names, each of which represents one operation). For instance:

 -- 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 {} + 

If you really need to use sed (for something that you can't even do with bash replacement syntax, ${var/pattern/substitution} ) then configure do_something as above, but replace the line inside for with with, for example:

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

Explanation: The above sed (gnu-specific) expression flips the last two extensions around, so it changes some.file.html.en to some.file.en.html . -r forces gnu sed to use the extended regex format, which I find more readable. <<< is a bagism that expands a word after it and passes it to stdin , somewhat similar to echo "$filename" | sed ... echo "$filename" | sed ... but without creating another subprocess.

0
source

You can name your find as follows:

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

This will lead to the execution of:

 html2haml thing.html.erb thing.html.haml 
0
source

Source: https://habr.com/ru/post/1490763/


All Articles