0

I have a very long sed statement that is 6 lines long strung together with -e. I am doing multiple replacements and writing to the same file.

It throws me an error for any sed statement over one line. How can I rectify this?

Some have suggested using variables to reduce the text length, but even still I think that solution won't work for me. Is there another simpler way to make this work?

Thanks.

edit: I just wasn't using my line breaks properly. Working script:

sed -e 's/horse.*/300/g' \
-e 's/goat.*/320/g' \
-e 's/dog.*/310/g' \ 
-e 's/wolf.*/330/g' < "$file1" > "$file2"

Thanks again.

OldMopMan
  • 59
  • 1
  • 5
  • 5
    Don't you think it would be awesome if we could see your sed statement? – timgeb Jun 18 '14 at 16:58
  • 1
    If you use multiple `-e` options, be sure to put a `\ ` (line continuation char.) at the end of all lines but the last. – mklement0 Jun 18 '14 at 17:00
  • What are you trying to achieve? Give an example. – Avinash Raj Jun 18 '14 at 17:00
  • 1
    Put your `\ ` at end of previous lines. (E.g.: `sed -e 's/horse.*/300/g' \ -e 's/goat.*/320/g'` instead of `sed -e 's/horse.*/300/g' \ -e 's/goat.*/320/g'`). `\ `s are meant to escape the (non-printed) `\n` end-of-line characters. – Qeole Jun 18 '14 at 17:30
  • @Qeole Thanks. When I tried that I recieved the error: syntax error near unexpected token 'new line'. This happened on the line that holds the first line break. – OldMopMan Jun 18 '14 at 17:44
  • I had not noticed that you're trying to give your expressions as distinct arguments to `sed`. Not sure it's possible. But you can pass multiple substitutions in a same `sed` expression, e.g.: `sed -e 's/horse.*/300/g ; s/goat.*/320/g'` (note that in this example you could replace `;` by `\`). – Qeole Jun 18 '14 at 17:48
  • @Qeole: Yes, it is possible to pass the overall `sed` script with _multiple_ `-e` options - that's even a POSIX feature - see http://man.cx/sed – mklement0 Jun 18 '14 at 18:14
  • 1
    Do not write ``! That was just to tell you to break line, as I can't do it in comments. Sorry for misleading. – Qeole Jun 18 '14 at 18:14

3 Answers3

1

See if this works:

sed 's/horse.*/300/g;s/goat.*/320/g;s/dog.*/310/g;s/wolf.*/330/g' filename.txt

Your script never ends because you are not feeding your SED command with a filename.

AKS
  • 16,482
  • 43
  • 166
  • 258
  • Thanks. I am feeding a file, I just forgot to write it in the example. I apologize for my incompetence. With that being said, the code you posted will work if I try it, but my actual code will not fit on one line like that. My sed statement is much longer than this example. Thanks again. – OldMopMan Jun 18 '14 at 17:50
  • @OldMopMan Just replace `;`s by `\ ` and line breaks. – Qeole Jun 18 '14 at 17:51
  • @Qeole I think that's what I have. I have line breaks at the end of each line. I have edited the code in my original post to reflect this. Thanks. – OldMopMan Jun 18 '14 at 18:00
  • @Qeole: If the entire script is a _single_ string, Do NOT put `\ ` at the end of lines - just use line breaks - sed recognizes normal line breaks as command separators; in other words: just replace `;` with line breaks. – mklement0 Jun 18 '14 at 18:08
  • @mklement0 You're right (again!). I think I had in mind the example of OP and got confused. – Qeole Jun 18 '14 at 18:16
  • @mklement0 I'm sorry, I'm a bit confused now. In my example, what do I want at the end of every line? I don't want \ ? – OldMopMan Jun 18 '14 at 18:45
  • @OldMopMan: You want `\ ` (note: due to formatting, it appears that there's a space after '\' - ignore that) at the _very end of every line_ (not even spaces after) _but the last_, with nothing after (except the _actual_ line break) - see my answer for an explanation. – mklement0 Jun 18 '14 at 18:49
  • @mklement0 Oh man, that's one of the first things I had tried. I must have typed it in wrong initially, but it works now. Thanks so much – OldMopMan Jun 18 '14 at 18:58
  • @Qeole ,@mklement0 Thanks for hanging in there with me. I really appreciate the help. – OldMopMan Jun 18 '14 at 19:00
  • OK, in that case, you can play with eval command and put everything (within ", " double quotes) in sed "..." < f1 > f2 reading from a file in the format that I mentioned above and then it wont be an ugly/long command, right! cheers – AKS Jun 18 '14 at 19:56
  • @OldMopMan: My pleasure; glad it's resolved. For the benefit of future readers: can you please create an answer yourself and put the resolution in there, then accept it (which you won't be able to do until 48 hours later)? – mklement0 Jun 18 '14 at 21:34
  • @ArunSangal: `eval` is rarely a good idea, but putting just the _script_ in a file is good alternative; you can load a script from a file with `sed -f`. – mklement0 Jun 18 '14 at 21:39
  • @mklement0 Yes, I can do that. I had edited my original post with the correct code, but will create a new answer as you have suggested. – OldMopMan Jun 23 '14 at 19:55
1

To summarize the various options for passing a script (program) [made up of multiple commands] to sed:

Let's assume you want to perform the following 2 commands:

  • s/a/A/
  • s/b/B/

on input 'ab', so as to get 'AB'.


Use a single-line string and separate the commands with ;:

echo 'ab' | sed 's/a/A/; s/b/B/'

Caveat: Some sed implementations - notably, FreeBSD/macOS sed require actual line breaks as part of certain commands, such as for defining labels and branching to them; typically, using separate -e options (see below) is an alternative.

For a comprehensive summary of the differences between GNU and FreeBSD/macOS sed, see this answer.


Use a multi-line string and use actual line breaks to separate the commands:

echo 'ab' | sed -e '
              s/a/A/
              s/b/B/
              '

Note: You can also use a here-document with the -f option - see below.


Use multiple -e options to pass the commands individually.

echo 'ab' | sed -e 's/a/A/' -e 's/b/B/'

If your option arguments are long and you want to spread the -e options across several lines, terminate all but the last line of the overall command with \ so as to signal to the shell that the command continues on the next line - note that the \ must be the very last character on the line (not even spaces or tabs are allowed after it):

echo 'ab' | sed -e 's/a/A/' \
                -e 's/b/B/'

Note that this is a general requirement of the shell and has nothing to do with sed itself.


Write your sed script to a file or use a here-document, and pass it to sed with the -f option:

# Create script file.
cat > myScript <<'EOF'
  s/a/A/
  s/b/B/
EOF

# Pass to `sed` with `-f`:
echo 'ab' | sed -f myScript

# Alternative, with a here-document via stdin, which requires
# the input to come from an actual file:
# Note: GNU sed also accepts `-f -`, but macOS sed does not.
echo 'ab' > test.txt
sed -f /dev/stdin test.txt <<'EOF'
  s/a/A/
  s/b/B/
EOF

Note:

  • All POSIX-compatible sed implementations should support all these methods.
  • You can even mix the -f and -e approaches, where the -e supplied command are appended to the overall script; e.g.: echo 'abc' | sed -f myScript -e 's/c/C/' # -> 'ABC'

  • If you use the variant with the here-document as the script (<<'EOF'...), use -f /dev/stdin to be portable (only GNU sed supports -f -).

mklement0
  • 382,024
  • 64
  • 607
  • 775
0
sed -e 's/horse.*/300/g' \  #replacing horse with horse300
-e 's/goat.*/320/g' \       #replacing goat with goat320   
-e 's/dog.*/310/g' \        #replacing dog with dog310
-e 's/wolf.*/330/g' < "$file1" > "$file2" #replacing wolf with wolf330 ; reading from file1, writing to file2

Above is the working code used for making multiple replacements using the same sed statement. Thanks again for all of the help.

OldMopMan
  • 59
  • 1
  • 5