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

2 Answers 2

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
    Commented Nov 12, 2020 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). Commented Nov 12, 2020 at 20:58
  • I updated with both syntaxes, thanks @StéphaneChazelas
    – thanasisp
    Commented Nov 12, 2020 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
    Commented Nov 14, 2020 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
    Commented Nov 15, 2020 at 0:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .