3

How do I find line starts with and replace complete line?

File output:

 xyz
 abc
 /dev/linux-test1/

Code:

output=/dev/sda/windows
sed 's/^/dev/linux*/$output/g' file.txt

I am getting below Error:

 sed: -e expression #1, char 9: unknown option to `s'

File Output expected after replacement:

 xyz
 abc
 /dev/sda/windows
ghoti
  • 45,319
  • 8
  • 65
  • 104
asteroid4u
  • 79
  • 2
  • 9
  • 1
    Possible duplicate of [Replace whole line containing a string using sed](https://stackoverflow.com/q/11245144/608639), [Replace whole line when match found with sed](https://stackoverflow.com/q/16440377/608639), [Use slashes in sed replace](https://stackoverflow.com/q/5864146/608639) and [Delete using a different delimiter with Sed](https://stackoverflow.com/q/1797906/608639) – jww Apr 28 '18 at 11:04

3 Answers3

1

Let's take this in small steps.

First we try changing "dev" to "other":

sed 's/dev/other/' file.txt
/other/linux-test1/

(Omitting the other lines.) So far, so good. Now "/dev/" => "/other/":

sed 's//dev///other//' file.txt
sed: 1: "s//dev///other//": bad flag in substitute command: '/'

Ah, it's confused, we're using '/' as both a command delimiter and literal text. So we use a different delimiter, like '|':

sed 's|/dev/|/other/|' file.txt
/other/linux-test1/

Good. Now we try to replace the whole line:

sed 's|^/dev/linux*|/other/|' file.txt
/other/-test1/

It didn't replace the whole line... Ah, in sed, '*' means the previous character repeated any number of times. So we precede it with '.', which means any character:

sed 's|^/dev/linux.*|/other/|' file.txt
/other/

Now to introduce the variable:

sed 's|^/dev/linux.*|$output|' file.txt
$output

The shell didn't expand the variable, because of the single quotes. We change to double quotes:

sed "s|^/dev/linux.*|$output|" file.txt
/dev/sda/windows
Beta
  • 96,650
  • 16
  • 149
  • 150
0

This might work for you (GNU sed):

output="/dev/sda/windows"; sed -i '\#/dev/linux.*/#c'"$output" file

Set the shell variable and change the line addressed by /dev/linux.*/ to it.

N.B. The shell variable needs to interpolated hence the ; i.e. the variable may be set on a line on its own. Also the the delimiter for the sed address must be changed so as not to interfere with the address, hence \#...#, and finally the shell variable should be enclosed in double quotes to allow full interpolation.

potong
  • 55,640
  • 6
  • 51
  • 83
-1

I'd recommend not doing it this way. Here's why.

Sed is not a programming language. It's a stream editor with some constructs that look and behave like a language, but it offers very little in the way of arbitrary string manipulation, format control, etc.

Sed only takes data from a file or stdin (also a file). Embedding strings within your sed script is asking for errors -- constructs like s/re/$output/ are destined to fail at some point, almost regardless of what workarounds you build into your sed script. The best solutions for making sed commands like this work is to do your input sanitization OUTSIDE of sed.

Which brings me to ... this may be the wrong tool for this job, or might be only one component of the toolset for the job.

The error you're getting is obviously because the sed command you're using is horribly busted. The substitute command is:

s/pattern/replacement/flags

but the command you're running is:

s/^/dev/linux*/$output/g

The pattern you're searching for is ^, the null at the beginning of the line. Your replacement pattern is dev, then you have a bunch of text that might be interpreted as flags. This plainly doesn't work, when your search string contains the same character that you're using as a delimiter to the options for the substitute command.

In regular expressions and in sed, you can escape things. You while you might get some traction with s/^\/dev\/linux.*/$output/, you'd still run into difficulty if $output contained slashes. If you're feeding this script to sed from bash, you could use ${output//\//\\\/}, but you can't handle those escapes within sed itself. Sed has no variables.

In a proper programming language, you'd have better separation of variable content and the commands used for the substitution.

output="/dev/sda/windows"
awk -v output="$output" '$1~/\/dev\/linux/ { $0=output } 1' file.txt

Note that I've used $1 here because in your question, your input lines (and output) appear to have a space at the beginning of each line. Awk automatically trims leading and trailing space when assigning field (positional) variables.

Or you could even do this in pure bash, using no external tools:

output="/dev/sda/windows"
while read -r line; do
  [[ "$line" =~ ^/dev/linux ]] && line="$output"
  printf '%s\n' "$line"
done < file.txt

This one isn't resilient in the face of leading whitespace. Salt to taste.

So .. yes, you can do this with sed. But the way commands get put together in sed makes something like this risky, and despite the available workarounds like switching your substitution command delimiter to another character, you'd almost certainly be better off using other tools.

ghoti
  • 45,319
  • 8
  • 65
  • 104