简体   繁体   中英

How to replace a specific line in a file with a string variable using the line number bash script?

Here is the contents of a target.txt file:

line1
line2
Environment=xLink=https://11111/route
line4
line5

I am trying to write a bash script that will find the number of the line containing 'https' and then replace this entire line with a new string variable obtained within the bash script, here is the bash script without the replacement line:

#!/bin/bash

x="12345"
route="/route"
x_route="${x}${route}"
x_init="Environment=xLink=https://"
new_line="${x_init}${x_route}"

echo "${new_line}"

to_replace_line_number=$(find target.txt -type f | xargs grep -n 'https' | cut -c1-2)

echo "${to_replace_line_number}"

targetfile=target.txt

echo "${targetfile}"

Invoking this script outputs the following as expected:

Environment=xLink=https://12345/route
3:
target.txt

Now, without the bash script, if I invoked:

sudo sed -i '3 c\Environment=xLink=https://12345/route' target.txt

The target.txt changes as desired to:

line1
line2
Environment=xLink=https://12345/route
line4
line5

But the goal is to automate, so I am trying to use sed command to do the job inside the bash script. So far I tried two methods, none of them worked.

Method 1: I added the following line to the bash script:

sudo sed -i "${to_replace_line_number}s/.*/${new_line}/" ${targetfile}

When I ran the script, it didn't work and I got this error:

sed: -e expression #1, char 2: : doesn't want any addresses

Method 2: I added the following command to the bash script:

sudo sed -i "${to_replace_line_number} c\${new_line}" ${targetfile}

When I ran the script, it didn't work and I got this error:

sed: -e expression #1, char 2: : doesn't want any addresses

What is that I am missing exactly? Any help is very much appreciated.

This is because you are taking out two characters when reading the line number.As a result, an extra ':' is popping up in the variable. Instead, take out only the one field and it should work fine.

Replace

to_replace_line_number=$(find target.txt -type f | xargs grep -n 'https' | cut -c1-2)

with

to_replace_line_number=$(find target.txt -type f | xargs grep -n 'https' | cut -c1)

\ is a special character, so when you use it in double quotes you have to escape it:

# Set example values and create a test file:
to_replace_line_number="3"
new_line="Environment=xLink=https://12345/route"
targetfile="test.txt"
printf 'line%d\n' {1..5} > "$targetfile"

# The actual command
sudo sed -i "${to_replace_line_number} c\\${new_line}" "${targetfile}"

This would make it equivalent to your manual invocation.

If you have wondered why the documentation for c appears to be weirdly formatted compared to r or y , it's because the linefeed after the \ is intentional. This is the POSIX way of doing it:

sudo sed -i "${to_replace_line_number} c\\
${new_line}" "${targetfile}"

There are a couple issues with the current code:

  • to_replace_line_number == 3: - NOTICE the colon ( : ); this is fed into the sed command like such: sed -i "3: c\.... and is generating the error message stating an issue with the 2nd character, ie, the :
  • as @thatotherguy has pointed out in his answer, the c\ option requires an escape character and an embedded carriage return... or...

Minimal changes to OPs current code:

# parse the `grep -n` by having `cut` pull everthing before the (first) `:`

$ to_replace_line_number=$(find target.txt -type f | xargs grep -n 'https' | cut -d":" -f1)
$ echo "${to_replace_line_number}"
3

# modification to @thatotherguy's `sed/c` suggestion to allow all code to go on a single line:

$ sed -i -e "${to_replace_line_number} c\\" -e "${new_line}" ${targetfile}
$ cat "${targetfile}"
line1
line2
Environment=xLink=https://12345/route
line4
line5

Instead of spawning the sub-process calls to get the line number, there are several ways sed can be used to find and replace the desired line.

One sed idea:

sed -i "s|^.*https.*$|${new_line}|" ${targetfile}

Where:

  • | - use pipe as sed delimiter since ${new_line} contains forward slashes
  • ^.*https.*$ - match any line that contains the string https
  • ${new_line} - replace the line with the contents of ${new_line}

After running the above:

$ cat target.txt
line1
line2
Environment=xLink=https://12345/route
line4
line5

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM