10

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?

slm
  • 363,520
  • 117
  • 767
  • 871
Prakki Rama
  • 169
  • 1
  • 10
  • 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 05 '14 at 09:34
  • 5
    The vi and sponge solutions also create temporary files to do their work... – daniel kullmann Nov 05 '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 06 '14 at 00:43

6 Answers6

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

garethTheRed
  • 33,289
  • 4
  • 92
  • 101
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
Sebastian
  • 8,677
  • 4
  • 39
  • 49
  • 1
    yours too ;-) Never heard of `sponge` before. – garethTheRed Nov 05 '14 at 09:35
  • I found it here: http://tools.suckless.org/sbase – Sebastian Nov 05 '14 at 09:37
  • 1
    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"`. – alexia Nov 05 '14 at 20:43
  • Also, if it's a bash script, you can just do `grep -q "$pattern" <<< "$line"`. – alexia Nov 05 '14 at 20:47
  • I think you forgot to define `pattern` and `replacement`, too. – alexia Nov 05 '14 at 20:50
  • I submitted an edit with some improvements. While it may not exactly be suitable for an edit, I'm hoping to save you some work. – alexia Nov 05 '14 at 20:54
  • thanks @nyuszika7h for suggesting multiple improvements. The script was a quick (sloppy) submission. If there are issues left, please let me know - I'm always keen on improving. E.g. I didn't know that `echo` has any issues. I'll check that. – Sebastian Nov 05 '14 at 21:07
  • sponge command seems to do similar function as redirection operator but changing contents in the same file. I could not find it in my linux distribution. – Prakki Rama Nov 06 '14 at 04:51
  • 1
    It's not a standard tool, but I think it deserves more publicity. See the link above , for download and source. – Sebastian Nov 06 '14 at 12:53
  • 1
    sponge is packaged in [moreutils](http://joeyh.name/code/moreutils/); Debian, Ubuntu and other distributions have it as an optional package in their repositories. – deltab Nov 07 '14 at 15:16
12

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
don_crissti
  • 79,330
  • 30
  • 216
  • 245
  • 1
    @mikeserv - wrt `ed` & temp file creation, it all depends on the implementation; see the posts [here](http://www.unix.com/answers-to-frequently-asked-questions/202403-edit-file-place-overwriting-original.html) by _bakunin_ and _alister_. – don_crissti Feb 09 '15 at 19:22
  • Is there some advantage to using `ed` over using `ex`? – Wildcard Apr 12 '16 at 02: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
6

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
Franki
  • 266
  • 1
  • 8
  • I can't see any globs there. – alexia Nov 05 '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 07 '14 at 00: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.

One Face
  • 348
  • 1
  • 14
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

Zombo
  • 1
  • 5
  • 43
  • 62