10

This question already has an answer here:

Is there a way to modify a file without writing the contents to another file, without sed and awk?

For example:

$ cat test.txt
aaa
aaa
bbb
ccc
ddd

Replacing using sed with -i option, sed -i 's/aaa/NNN/g' test.txt will produce the following:

NNN
NNN
bbb
ccc
ddd

How to do that without awk and sed?

marked as duplicate by Gilles text-processing Aug 29 '16 at 17:48

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 7
    by the way, sed -i internally writes to a temporary file and then moves it into the place of the original file. The option title --in-place is a little misleading. – Sebastian Nov 5 '14 at 9:34
  • 5
    The vi and sponge solutions also create temporary files to do their work... – daniel kullmann Nov 5 '14 at 11:39
  • 1
    My intention to post this question is that atleast the user should not manually create a new file and write the contents. Thank you for simple answers and comments. – Prakki Rama Nov 6 '14 at 0:43
18

You can use a vi script:

$ vi test.txt -c '%s/aaa/NNN/ | wq'
$ cat test.txt
NNN
NNN
bbb
ccc
ddd

You're simply automating what would normally be entered when using vi in command mode (accessed using Esc: usually):

% - carry out the following command on every line:

s/aaa/NNN/ - subtitute aaa with NNN

| - command delimiter

w - write changes to file

q - quit

  • seems very neat and simple. – Prakki Rama Nov 5 '14 at 9:57
  • 1
    I was using your script for replacing tab with comma. vi test.txt -c '%s/\t/,/**g** | wq' did well for multiple columns. Without g, it was only replacing the first instance. Thank you. – Prakki Rama Dec 3 '14 at 8:30
14

Using sponge:

#!/bin/bash

pattern='aaa'
replacement='NNN'

while read -r line
do                                                                              
  printf '%s\n' "${line//$pattern/$replacement}"
done < "${1}"

Call with:

./script.sh test.txt | sponge test.txt
  • 1
    yours too ;-) Never heard of sponge before. – garethTheRed Nov 5 '14 at 9:35
  • I found it here: tools.suckless.org/sbase – Sebastian Nov 5 '14 at 9:37
  • That script suffers from multiple issues. (1) read line should be read -r line. (2) The $() in the if is redundant. I'm surprised if that even works. (3) echo suffers from a lot of portability issues even within bash. Use printf '%s\n' "${var}" instead. (4) $1 should be wrapped in double quotes: "$1". – nyuszika7h Nov 5 '14 at 20:43
  • 1
    It's not a standard tool, but I think it deserves more publicity. See the link above , for download and source. – Sebastian Nov 6 '14 at 12:53
  • 1
    sponge is packaged in moreutils; Debian, Ubuntu and other distributions have it as an optional package in their repositories. – deltab Nov 7 '14 at 15:16
13

With ed, the line editor:

ed -s test.txt <<< $',s/pattern/replace/g\nw\nq'

or

ed -s test.txt <<IN
,s/pattern/replace/g
w
q
IN

or

printf '%s\n' ,s/pattern/replace/g w q | ed -s test.txt
  • 1
    @mikeserv - wrt ed & temp file creation, it all depends on the implementation; see the posts here by bakunin and alister. – don_crissti Feb 9 '15 at 19:22
  • Is there some advantage to using ed over using ex? – Wildcard Apr 12 '16 at 2:29
  • @Wildcard - I've never had any interest in ex (and as a result, I've never used it - same for vi). I only know that on some exotic setups you may find ed but not ex. Other than that I'm afraid I can't answer your question. Someone like Stéphane - who knows the ins and outs of both - would be the right guy to ask. – don_crissti Apr 12 '16 at 12:12
  • Both are specified in POSIX, and ex is more flexible. But thanks for explaining. :) – Wildcard Apr 12 '16 at 19:05
5

If you are using bash or ksh, you can use pattern substitution for shell variables. Note however, that basic shell globs are less powerful and extended shell globs have some features that sed doesn't and vice versa. For more details, see 'Parameter Expansion' in man 1 bash:

t=$(< test.txt); printf '%s\n' "${t//aaa/NNN}" >test.txt

Extended shell globs are disabled by default, so you may need to explicitly enable them:

shopt -s extglob
  • I can't see any globs there. – nyuszika7h Nov 5 '14 at 20:55
  • @nyuszika7h true, although it is possible if you want to, like so: printf '%s\n' "${t//+(a|d)/NNN}" if you wanted to replace all runs of 'a's or 'd's with 'NNN' – Franki Nov 7 '14 at 0:54
4

You can also use perl

perl -pi -e 's/aaa/bbb/g' file.txt

This will give the output you desire.

You can also backup your original file automatically using i.bak instead of i. This will create a backup named file.txt.bak.

2

You can use Vim in Ex mode:

ex -sc '%s/aaa/NNN/|x' test.txt
  1. % select all lines

  2. s substitute

  3. x save and close

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