4

Lets say for example I have a file.txt containing 3 lines:

hello
hello
hello

I have a variable called line=1 and a variable string substitute=hello1. I want to replace the string in the line described by the variable line with my new string variable called substitute.

The resultant change to files.txt would look like:

hello1
hello
hello

How would I use sed to do this? Or awk? If thats what you are in to, as long as I can input variables that I can change in a while loop.

2
  • 1
    You seem to be performing text substitution in a shell script. While many possibilities immediately come to mind to help you, contributors will be able to help you better if you post at least an excerpt of the script. Also, can you edit your post to add more information about the text to be found and replaced? Is it "simply" the entire line you want to replace or are there other boundary conditions?
    – AdminBee
    Nov 2, 2020 at 13:23
  • 2
    Also, you aren't the first person ever trying to do that. The same question has been already asked and answered here or at StackOverflow.
    – choroba
    Nov 2, 2020 at 13:23

7 Answers 7

7

With perl:

export line substitute
perl -pi -e '$_ = $ENV{substitute} . "\n" if $. == $ENV{line}' file.txt

same with GNU awk:

export line substitute
gawk -i /usr/share/awk/inplace.awk '
  NR == ENVIRON["line"] {$0 = ENVIRON["substitute"]}
  {print}' file.txt

Using sed is not ideal here, even with those that support a perl-like -i option, as we'd need to do some post-processing on the value of $substitute if it may contain backslash, &, the s command delimiter or newline characters. See How to ensure that string interpolated into `sed` substitution escapes all metachars for details on that.

In gawk, do not use -i inplace as gawk tries to load the inplace extension (as inplace or inplace.awk) from the current working directory first, where someone could have planted malware. The path of the inplace extension supplied with gawk may vary with the system, see the output of gawk 'BEGIN{print ENVIRON["AWKPATH"]}'

1
  • 1
    Consider line="$line" substitute="$substitute" gawk '...' on a single line so you don't have to export the variables.
    – Ed Morton
    Nov 2, 2020 at 18:00
2

Assuming line >0 and always is int, you could do:

<infile head -n$(($line-1)); printf '%s\n' "$substitute"; tail -n +$(($line+1)) infile

Note that this will append the $substitute value to the end of file if line_number $line > file's_number_of_lines

0
2

Without further information, the following awk program should work:

awk -v where="$line" -v what="$substitute" 'FNR==where {print what; next} 1' file.txt
  • It will import the shell variables $line and $substitute into awk variables where and what.
  • When processing the file, it will by default simply print the current line ( the seemingly stray 1 outside the rule block { ... }).
  • When the per-file line counter FNR is equal to the line number stored in where, we print the substitute string what instead, and skip execution to the next input line.

Note that awk doesn't edit files in-place, so you have to redirect the output and rename the result file once you are satisfied. Alternatively, with sufficiently new versions of GNU awk, you can use the -i inplace option to modify the file in-place.

Note that (as pointed out by αғsнιη/Stéphane Chazelas/Ed Morton) the above will show erratic behavior if your substitute string contains the literal \, since this method expands escape sequences. In that case, the awk-based alternative solution in Stéphane Chazelas' answer with exporting the variable and referencing it as ENVIRON["substitute"] would do the trick (see this answer for more information).

0
1

Assuming the value of $substitute doesn't contain special characters, you can simply do:

line=1
substitute=hello1
sed -i "${line}s/.*/$substitute/"  file.txt
1
$   line=1 substitute=hello1
$ sed -nf - <<eof file 
  $line r $(t=$(mktemp);printf '%s\n' "$substitute" > "$t";echo "$t")
  $line!p
eof

We use a heredoc to pass the sed code. The sed code is reading a file and placing jts contents on the stdout and suppressing the original line by virtue of the -n option.

0

If:

  • $line contains an actual line number of the file (is non-zero, is not greater than the last line), and
  • $substitute is not equal to a single period (.)

then you could use an ed script:

printf '%dc\n%s\n.\nw\nq' "$line" "$substitute" | ed -s input

This sends several lines of commands as input to ed:

  1. change the line represented by $line
  2. to the $substitute value
  3. end the change with .
  4. write the changed file back to disk
  5. quit ed

Of course, since there's no error-checking, $line and $substitute need to abide by the assumptions at the top. A surrounding shell script could sanity-check the $line variable, but entering a bare . as input to ed would require additional effort.

0
sed ""$line"s/.*/"$substitute"/g" filename
1
  • 2
    Please take note of the link in Stéphane's answer regarding the need to escape arbitrary data in (e.g.) the $substitute variable.
    – Jeff Schaller
    Nov 3, 2020 at 17:14

You must log in to answer this question.

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