`sed` works, but `sed -i` returns an error - linux

This line works:
sed -r -e 's/^([^#a-z]+)localhost/\1hostname.domain hostname localhost/' /etc/hosts
But adding the itty option "i":
sed -ir -e 's/^([^#a-z]+)localhost/\1hostname.domain hostname localhost/' /etc/hosts
Results in:
sed: -e expression #1, char 60: invalid reference \1 on `s' command's RHS
Can someone tell me what's going on??

You've turned off the -r (extended syntax) option, because what you append to -i isn't more options, but an optional backup suffix. From the manpage:
-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if extension supplied)
So just separate them:
sed -i -r -e 's/^([^#a-z]+)localhost/\1hostname.domain hostname localhost/' /etc/hosts

I think you should separate options: write -i -r and not -ir, since -i may interpret r as the suffix to append to the old unedited file, so that -r is not taken

"-ir" means something different from "-i -r" or "-ri", see the man page.

Related

How to apply my sed command to some lines of all my files?

I've 95 files that looks like :
2019-10-29-18-00/dev/xx;512.00;0.4;/var/x/xx/xxx
2019-10-29-18-00/dev/xx;512.00;0.68;/xx
2019-10-29-18-00/dev/xx;512.00;1.84;/xx/xx/xx
2019-10-29-18-00/dev/xx;512.00;80.08;/opt/xx/x
2019-10-29-18-00/dev/xx;20480.00;83.44;/var/x/x
2019-10-29-18-00/dev/xx;3584.00;840.43;/var/xx/x
2019-10-30-00-00/dev/xx;2048.00;411.59;/
2019-10-30-00-00/dev/xx;7168.00;6168.09;/usr
2019-10-30-00-00/dev/xx;3072.00;1036.1;/var
2019-10-30-00-00/dev/xx;5120.00;348.72;/tmp
2019-10-30-00-00/dev/xx;20480.00;2033.19;/home
2019-10-30-12-00;/dev/xx;5120.00;348.72;/tmp
2019-10-30-12-00;/dev/hd1;20480.00;2037.62;/home
2019-10-30-12-00;/dev/xx;512.00;0.43;/xx
2019-10-30-12-00;/dev/xx;3584.00;794.39;/xx
2019-10-30-12-00;/dev/xx;512.00;0.4;/var/xx/xx/xx
2019-10-30-12-00;/dev/xx;512.00;0.68;/xx
2019-10-30-12-00;/dev/xx;512.00;1.84;/var/xx/xx
2019-10-30-12-00;/dev/xx;512.00;80.08;/opt/xx/x
2019-10-30-12-00;/dev/xx;20480.00;83.44;/var/xx/xx
2019-10-30-12-00;/dev/x;3584.00;840.43;/var/xx/xx
For some lines I've 2019-10-29-18-00/dev and for some other lines, I've 2019-10-30-12-00;/dev/
I want to add the ; before the /dev/ where it is missing, so for that I use this sed command :
sed 's/\/dev/\;\/dev/'
But How I can apply this command for each lines where the ; is missing ? I try this :
for i in $(cat /home/xxx/xxx/xxx/*.txt | grep -e "00/dev/")
do
sed 's/\/dev/\;\/dev/' $i > $i
done
But it doesn't work... Can you help me ?
Could you please try following with GNU awkif you are ok with it.
awk -i inplace '/00\/dev\//{gsub(/00\/dev\//,"/00;/dev/")} 1' *.txt
sed solution: Tested with GNU sed for few files and it worked fine.
sed -i.bak '/00\/dev/s/00\/dev/00\;\/dev/g' *.txt
This might work for you (GNU sed & parallel):
parallel -q sed -i 's#;*/dev#;/dev#' ::: *.txt
or if you prefer:
sed -i 's#;*/dev#;/dev#' *.txt
Ignore lines with ;/dev.
sed '/;\/dev/{p;d}; s^/dev^;/dev^'
The /;\/dev/ check if the line has ;/dev. If it has ;/dev do: p - print the current line and d - start from the beginning.
You can use any character with s command in sed. Also, there is no need in escaping \;, just ;.
How I can apply this command for each lines where the ; is missing ? I try this
Don't edit the same file redirecting to the same file $i > $i. Think about it. How can you re-write and read from the same file at the same time? You can't, the resulting file will be in most cases empty, as the > $i will "execute" first making the file empty, then sed $i will start running and it will read an empty file. Use a temporary file sed ... "$i" > temp.txt; mv temp.txt "$i" or use gnu extension -i sed option to edit in place.
What you want to do really is:
grep -l '00/dev/' /home/xxx/xxx/xxx/*.txt |
xargs -n1 sed -i '/;\/dev/{p;d}; s^/dev^;/dev^'
grep -l prints list of files that match the pattern, then xargs for each single one -n1 of the files executes sed which -i edits files in place.
grep for filtering can be eliminated in your case, we can accomplish the task with a single sed command:
for f in $(cat /home/xxx/xxx/xxx/*.txt)
do
[[ -f "$f" ]] && sed -Ei '/00\/dev/ s/([^;])(\/dev)/\1;\2/' "$f"
done
The easiest way would be to adjust your regex so that it's looking a bit wider than '/dev/', e.g.
sed -i -E 's|([0-9])/dev|\1;/dev|'
(note that I'm taking advantage of sed's flexible approach to delimiters on substitute. Also, -E changes the group syntax)
Alternatively, sed lets you filter which lines it handles:
sed -i '/[0-9]\/dev/ s/\/dev/;/dev/'
This uses the same substitution you already have but only applied on lines that match the filter regex

Using sed in a script

I am trying to insert a line of text under a line of existing text in a file as per the following excerpt:
#!/bin/bash
MATCH='Text_To_Look_For'
INSERT='Text_To_Add'
FILE='/home/user/test.txt'
echo "Modifying file..."
sed -i 's/"$MATCH/$MATCH"\n"$INSERT"/' $FILE
echo "Done."
I have tried all sorts of combinations of / ' and " but cant seem to make it work. The script will run now but not insert anything. In other formats I keep getting errors relating to:
unterminated s command
sed: -e expression #1, char 35: unknown option to `s'
You didn't say what version of sed you're using, I've seen something similar on MacOS, where the standard sed is rather poor... Worked around by installing a GNU Sed (invoked later as gsed).
Anyway, can you give a try with sed a command (which stands for append):
#!/bin/bash
MATCH='Text_To_Look_For'
INSERT='Text_To_Add'
FILE='/tmp/xxx'
echo "Modifying file..."
sed -i "/$MATCH/a $INSERT" $FILE
echo "Done."
Best regards,
Jarek
The -i option to sed should be followed by a file extension, see man sed: -i extension - Edit files in-place, saving backups with the specified extension.
It is trying to use your s command as a filename extension!
Try this:
sed -i .bak s/$MATCH/$MATCH\n$INSERT/ "$FILE"
Note that the -i option is non-standard and sed versions vary. For example you might have to remove the space:
sed -i.bak s/$MATCH/$MATCH\n$INSERT/ "$FILE"
An alternative to the -i is:
if sed s/$MATCH/$MATCH\n$INSERT/ "$FILE" > "$FILE.bak"
then
mv "$FILE.bak" "$FILE"
fi

How to to delete a line given with a variable in sed?

I am attempting to use sed to delete a line, read from user input, from a file whose name is stored in a variable. Right now all sed does is print the line and nothing else.
This is a code snippet of the command I am using:
FILE="/home/devosion/scripts/files/todo.db"
read DELETELINE
sed -e "$DELETELINE"'d' "$FILE"
Is there something I am missing here?
Edit: Switching out the -e option with -i fixed my woes!
You need to delimit the search.
#!/bin/bash
read -r Line
sed "/$Line/d" file
Will delete any line containing the typed input.
Bear in mind that sed matches on regex though and any special characters will be seen as such.
For example searching for 1* will actually delete lines containing any number of 1's not an actual 1 and a star.
Also bear in mind that when the variable expands, it cannot contain the delimiters or the command will break or have unexpexted results.
For example if "$Line" contained "/hello" then the sed command will fail with
sed: -e expression #1, char 4: extra characters after command.
You can either escape the / in this case or use different delimiters.
Personally i would use awk for this
awk -vLine="$Line" '!index($0,Line)' file
Which searches for an exact string and has none of the drawbacks of the sed command.
You might have success with grep instead of sed
read -p "Enter a regex to remove lines: " filter
grep -v "$filter" "$file"
Storing in-place is a little more work:
tmp=$(mktemp)
grep -v "$filter" "$file" > "$tmp" && mv "$tmp" "$file"
or, with sponge (apt install moreutils)
grep -v "$filter" "$file" | sponge "$file"
Note: try to get out of the habit of using ALLCAPSVARS: one day you'll accidentally use PATH=... and then wonder why your script is broken.
I found this, it allows for a range deletion with variables:
#!/bin/bash
lastline=$(whatever you need to do to find the last line)` //or any variation
lines="1,$lastline"
sed -i "$lines"'d' yourfile
keeps it all one util.
Please try this :
sed -i "${DELETELINE}d" $FILE

Pipe grep output into sed to replace an entire line

I'm trying to pipe the output of a grep command into the 'replace me with' value in a sed command. I've tried xargs and just a pipe, but I can't seem to get it working. All of the examples I've found on stack overflow assume that I know the end result of my grep command. Here is an example of what I'm trying to do.
cat /etc/sysconfig/network | grep HOSTNAME | grep -i s/greppedline/"HOSTNAME=something"/
Effectively, I won't know the full contents of the line that I need to replace, just the fact that HOSTNAME will be in it. Is there a away to do this as a one-liner without creating a variable from the grep commmand?
I think you're trying to do like this,
sed '/HOSTNAME/s/.*/"HOSTNAME=something"/' /etc/sysconfig/network
Add the inline edit -i option to save the changes made.
sed -i.bak '/HOSTNAME/s/.*/"HOSTNAME=something"/' /etc/sysconfig/network
sed '/HOSTNAME/ c\
"HOSTNAME=something"/' /etc/sysconfig/network
or
sed 's/.*HOSTNAME.*/"HOSTNAME=something"/' /etc/sysconfig/network

Apply Linux command recursively to all files in directory of single (.sh) type

I am trying to apply this command sed -i -e 's/\r$//' to all Shell (.sh) files in my directory I have tried:
sed -i -e -R 's/\r$//' *.sh
But this throws an unknown character error.
Any help would be great cheers!
This is an argument order issue. -e expects the expression/script as the next argument and you are giving it -R.
Swap those two arguments sed -i -e '....'.
That being said the dos2unix program exists for this purpose as well.

Resources