Is there any alternative to the "sed -i" command in Solaris?

24,376

Solution 1

Use ed. It's available on most platforms and it can edit your files in-place.
Since sed is based on ed the syntax for replacing patterns is similar:

ed -s infile <<\IN
,s/old/new/g
w
q
IN

Solution 2

If you cannot install GNU sed, use:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

This uses redirection to send the output of sed to a temporary file. If sed completes successfully, then this overwrites abc.txt with the temporary file.

As can be seen from the source code for GNU sed, this is exactly what sed -i does. So, this is just about as efficient as sed -i.

If there is a chance that abc.tmp already exists, then you may want to use mktemp or a similar utility to generate the unique name for the temporary.

Solution 3

First, you should know that the i\ command you're referring to is for inserting a line of text—not for saving the edited text back to the file. There is no POSIX-specified way to use sed for that.

What you can do is use ex, which is specified by POSIX:

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

The x command is for "save and exit." You can also use wq but x is shorter.

The % sign at the start of the substitute command means, "Apply this command to every line in the buffer." You could use 1,$s/find/replace/g also.


One major difference between ex and sed is that sed is a stream editor. It only operates sequentially, line by line. ex is far more flexible than that, and in fact you can do interactive text file editing directly in ex. It is the immediate predecessor to vi.

Solution 4

If you want the equivalent of sed -i.bak, it's pretty simple.

Consider this script for GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

We can simply replace the marked line with

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

This moves the existing file to be a backup, and writes a new file.

If we want the equivalent of

sed -i -e "$script" "$dir"  # no backup

then it's slightly more complex. We can open the file for reading as standard input, then unlink it, before directing sed's output to replace it:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

We do this in a sub-shell, so that our original stdin is still available after this. It's possible to switch inputs and switch back without a subshell, but this way seems clearer to me.

Note that we're careful to copy first, rather than creating a new foo file - this is important if the file is known by more than one name (i.e. has hard links) and you want to be sure that you don't break the links.

Solution 5

Using sed and no visible temporary file:

You can avoid creating a separate visible "temp file":

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Explanation

Unix-like systems don't actually remove the file's contents from the disk until it's both unlinked in the filesystem, and not open in any process. So you can do exec 3< to open the file in the shell for reading on file descriptor 3, rm the file (which unlinks it from the file system), and then call sed with file descriptor 3 used as its input.

Note that this is very different from this:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

The difference is that when you do it in one command, the shell just opens the same file for both reading, and for writing with the option to truncate the file - since it's still the same file, you lose the contents. But if you open it for reading, then rm it, then open the same pathname for writing, you're actually creating a new file at the same pathname (but at a new inode and disk location, since the original is still open): so the contents are still available.

Then once you're done, you can close the file descriptor you opened previously (that's what the exec 3<&- special syntax does), which releases the original file so the operating system can delete (mark as unused) its disk space.

Caveats

There's a few things to keep in mind about this solution:.

  1. You only get one "go" through the contents - there's no portable way for a shell to "seek" back in the file descriptor - so once a program reads some of the contents, other programs will only see the remainder of the file. And sed will read the entire file.

  2. There's a small chance of your original file being lost if your shell/script/sed gets killed before it's done.

Share:
24,376

Related videos on Youtube

tpsaitwal
Author by

tpsaitwal

Hello I am an excellent programmer, currently working with Deutsche Bank Of India. Placed in Pune and I love to look into and solve questions on Web development and take an interest in developing the same. Look forward to an intense scene. "I would agree with you, but that would make us both wrong." Respect and Loyalty.

Updated on September 18, 2022

Comments

  • tpsaitwal
    tpsaitwal over 1 year

    I have a requirement in my project to replace some existing text in a file like foo with some other text like fooofoo:

    abc.txt
    name
    foo
    foo1
    

    So I tried:

    sed -i "s/foo/fooofoo/g" abc.txt
    

    However I get this error:

    sed: illegal option -- i

    I found in the manual that I have to use:

    sed -i\ "s/foo/fooofoo/g" abc.txt
    

    However this is not working either.

    I found alternatives in perl and awk also but a solution in Solaris sed would be much appreciated.

    I am using this version of bash:

    GNU bash, version 3.2.57(1)-release (sparc-sun-solaris2.10)

    • MusiGenesis
      MusiGenesis over 7 years
      On Solaris 11 and later, simply use /usr/gnu/bin/sed to get -i support.
  • tpsaitwal
    tpsaitwal over 7 years
    Thanks for help. However Is there any option in solaris by which we can update the existing file without creating a temp file?
  • Jeff Schaller
    Jeff Schaller over 7 years
    Hate to break it to you, but even sed creates a temporary file. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
  • Toby Speight
    Toby Speight over 7 years
    You can do it if you don't care about hard links - see my example. There's still a temporary file, but it's hidden (unnamed). Without a temporary, you'd need to use ed, since it reads the whole file into memory.
  • Wildcard
    Wildcard over 7 years
    Why ed instead of ex, just curious? (I know both are POSIX compliant and I've linked to the specs. ;) )
  • don_crissti
    don_crissti over 7 years
    @Wildcard - I could ask the opposite - why ex ? To answer your question: since I never use ex I'm not familiar with it. btw, I've seen setups where ed was present but ex was not (but not the opposite)... the most notable being my laptop :)
  • Wildcard
    Wildcard over 7 years
    Good answer. :) My answer: Since I use Vim all the time, I am very familiar with ex commands. All the vi commands you can type that start with a colon are ex commands. There are of course a few Vim-specific extensions, but just the core set of commands is incredibly powerful and flexible and is plenty for scripted edits.
  • mtraceur
    mtraceur over 7 years
    To the downvoter: While I'm sure just leaving a downvote makes you feel good about yourself, I'd appreciate if you'd contribute more by providing feedback as to what issues you have with this answer, how it can be improved, etc.
  • c4f4t0r
    c4f4t0r over 7 years
    maybe you can use perl -i
  • mtraceur
    mtraceur over 7 years
    @c4f4t0r: Sorry for the late reply: Yes, perl -i is another good option. The question specifically requested a solution compatible with Solaris' sed, in contrast to solutions with perl and awk that they mentioned already finding. Plus, my answer was mainly intended to add something that I thought was useful to know and missing from any of the other answers.
  • mid
    mid over 4 years
    My Manjaro system has ex and yet not ed.