2

I'm writing the shell script that should process through some text, I've got the text like that:

read build { file1 file2 file3 }
check build { file2 file3 file4 }
read build { file4 file5 file6 }

I want to add some text before all files - that means words, that are included between brackets in 'read build' lines, what do you think is the best idea to achieve that? The results should look like that:

read build { MY_ADDED_WORDfile1 MY_ADDED_WORDfile2 MY_ADDED_WORDfile3 }
check build { file2 file3 file4 }
read build { MY_ADDED_WORDfile4 MY_ADDED_WORDfile5 MY_ADDED_WORDfile6 }

I was trying with greping that lines, next parsing it to array and concatenate this "file" strings with the word I want to add, but I think that is not a "smart" solution. Thanks for your help.

Input files look like this:

set build { file1 file2 file3 file4 }
check $build

read build -new -f $build
read build -new -f { fileA fileB fileC fileD }

set build {file5 file6 file7 }

read build -old -f $build
read build -old -f { fileX fileZ fileD }

check_that_building

Output should look like that:

set build { file1 file2 file3 file4 }
check $build

read build -new -f $build
read build -new -f { MY_ADDED_WORDfileA MY_ADDED_WORDfileB MY_ADDED_WORDfileC MY_ADDED_WORDfileD }

set build {file5 file6 file7 }

read build -old -f $build
read build -old -f { MY_ADDED_WORDfileX MY_ADDED_WORDfileZ MY_ADDED_WORDfileD }

check_that_building

There is always a space before opening bracket but it can be a situation that there will be not a space after opening bracket, there can a space before closing bracket but it's not always. Possible cases:

read build -old -f { fileX fileZ fileD }
read build -old -f {fileX fileZ fileD }
read build -old -f { fileX fileZ fileD}
read build -old -f {fileX fileZ fileD}
  • Please edit your question and show us more of the input file. Are there lines that contain { } but which should be ignored or should all lines with { } be modified? Can the target strings (e.g. file1) appear outside the { } or should all cases of file1 be changed? – terdon Nov 11 '15 at 19:28
  • Please edit your question to be consistent.  You still say that you want to focus on "read that" lines, but your new sample input doesn't have any such lines.  Also, it doesn't help much if you show more of the input file but don't show the corresponding output (but shame on you, @terdon, for not specifically asking for that).  Also, are you saying that there isn't always a space after the {?  Can we assume, at least, that there is always a space after the { on the "read ..." lines (which are the one that must be modified)? – G-Man Nov 11 '15 at 19:57
  • edited, sorry for being imprecise – TheOmniano Nov 11 '15 at 20:06
1

I couldn’t immediately figure out how to do it in sed without assuming that there is at least one character that is known never to appear in the input.  I assumed that # will never appear in the input (or in your added word).  This seems to work:

sed '/read build/ {
        s/{/{ /
        : fruit
        s/\({.*\) \([^}# ][^ ]*\)/\1#MY_WORD\2/
        t fruit
        s/#/ /g
        s/{ /{/
}'

On lines that contain read build it first inserts a space after the {. Then it searches for a space that is somewhere after a { and immediately before a word (presumably a filename).  It replaces the space with #, inserts your word, and goes back and looks for more.  (fruit is an arbitrary loop label.)  Once it’s found them all, it turns all the # characters back to spaces, and removes the space that it inserted (after the {).

In addition to the bit about # not occurring in the input, this assumes that

  • } is the last non-blank character on each read build line, and
  • whitespace is spaces only; no tabs.

In awk:

awk '/read build/ {
        in_braces=0
        for (i = 1; i <= NF; i++) {
                if ($i == "{") in_braces=1
                else if (substr($i,1,1) == "{") {
                        $i = "{MYWORD" substr($i,2)
                        in_braces=1
                }
                else if ($i == "}") in_braces=0
                else if (in_braces) $i = "MY_WORD" $i
        }
      }
      { print }'

For each read build line, it loops through all the words (fields) in the line.  It uses a state variable (in_braces) to keep track of whether it is between a { and a }; if it is, it modifies each word to begin with your added word.  Note that it has to handle two slightly different cases:

  • if a word is {, set the flag to start modifying all subsequent words, and
  • if a word begins with {, it is actually a compound of the form {fileX, so modify it to be the concatenation of {, the added word, and the fileX filename.  And also set the flag to modify all subsequent words.

While this allows tabs as word separators, it has the weakness that it collapses white space to a single space.  So for example, the input

read build    {    file1    file2    file3    }

would produce the output

read build { MY_WORDfile1 MY_WORDfile2 MY_WORDfile3 }

Further, this assumes that

  • the { is at the beginning of a word (i.e., it has whitespace before it), and
  • either } is the last non-blank character on each read build line, or it is a separate word (i.e., it has whitespace before and after)

It allows multiple sets of braces; e.g.,

read build { file1 file2 file3 } text to be left alone { file4 file5 file6 }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.