1

I'm trying to use q command in sed (to mimic head -n and only get the first few lines) and then s/ to substitute only on those lines a pattern.

If I omit the first expression, the substitution does exactly what I want, but when I try to apply the substitution only on the first line, only the first substitution takes place. I'm using sed and not head because I'd like to use the -i command later.

sed -e '2q' -e 's/\([0-9]\{4\}[[:space:]]\)\{3\}/XXXX XXXX XXXX /g' file
0
3

The order of the expressions matters. Here is an example:

cat file
a
a
a
a

< file sed '2q; s/a/b/g'
b
a

< file sed 's/a/b/g; 2q'
b
b

So you have to quit after you have replaced the second line. Currently you quit before applying the expression for the substitution. Or if you prefer the -e syntax:

< file sed -e 's/a/b/g' -e '2q'

A slighly different case:

If you want to apply a substitution only for the first 2 lines, but print the whole file, probably using -i together to do it in-place, you can do this:

< file sed '1,2 s/a/b/g' file
b
b
a
a

We match a line-range, 1,2 and for that range only, we execute the expression following. If we have many expressions to run for this range, we enclose them inside {}, like this:

< file sed '1,2{s/a/b/g; s/c/d/g;}' file
3
  • For some reason this never crossed my mind.Thank you !
    – Paul
    Nov 12 '20 at 19:06
  • 1
    Note that you don't need the braces if you only run one command on that address range. (sed '1,2 s/a/b/g' would be enough). Nov 12 '20 at 20:58
  • I updated with both syntaxes, thanks @StéphaneChazelas
    – thanasisp
    Nov 12 '20 at 21:06
0

Don't sacrifice your software for the sake of -i. Doing head -n file > tmp && mv tmp file is just not a big deal and it's what sed is doing internally. Also, just like GNU sed has -i, GNU awk has -i inplace and will generally let you write a clearer and easier to enhance script than trying to do anything other than a simple s/old/new/ with sed.

It's not clear whether you want to modify all lines and only print the first 2 or print all lines but only modify the first 2 or something else so here's a couple of possibilities:

  1. Only print the first 2 input lines and modify both of them:
awk '{gsub(/\([0-9]{4}[[:space:]]\){3}/,"XXXX XXXX XXXX "); print} NR==2{exit}' file
  1. Print all input lines but only modify the first 2 of them:
awk 'NR<=2{gsub(/\([0-9]{4}[[:space:]]\){3}/,"XXXX XXXX XXXX ") {print}' file

and with GNU awk for "inplace" editing like with GNU sed -i it'd just be:

awk -i inplace '...' file
2
  • I really appreciate your feedback, this was an assignment so we were "forced" to use sed, I also find awk more intuitive. I thought -i from sed was doing it while iterating through the files (so more efficient), never knew it was creating a temporary file, thanks for clearing that up.
    – Paul
    Nov 14 '20 at 20:41
  • Yeah, all the tools that claim to do "inplace" editing use a temp file behind the scenes except for ed which reads the whole file into memory before it starts working on it (which you can also do with any awk).
    – Ed Morton
    Nov 15 '20 at 0:44

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.