How to Use "sed" Bash Command - linux

I am having difficulties replacing a string in c program file with the content of a variable in a bash file. The idea is to copy the address of Linux kernel sys_call_table and then use it in my C program to intercept system calls. I found a couple of examples online, but none of them have worked for me so far. So any help will be greatly appreciated.
Here is the content of my bash file, "bashFile.sh"
TABLE=$(grep sys_call_table /boot/System.map-$(uname -r) |awk '{print $1}')
sed -i "s/myTABLE/{$TABLE}/g" my_LKM.c
When I run "sudo sh bashFile.sh" command, nothing happen. The string myTable in C file does not get replaced. However, when I try the following variation:
TABLE=$(grep sys_call_table /boot/System.map-$(uname -r) |awk '{print $1}')
sed -i 's/myTABLE/{$TABLE}/g' my_LKM.c
the myTABLE string get replaced with {$TABLE} instead of the content of the variable TABLE (sys_call_table address). I tried debugging with "echo $TABLE" to see if TABLE content is the address of sys_call_table and it worked. So, I concluded that the problem might be the syntax of sed command. However, I do not know how to fix it at this time. Thank you in advance for your help.
P.S. Below is the content of myLKM.c file:
unsigned long *sys_call_table;
sys_call_table = (unsigned long *)myTABLE;

First of all, don't use sudo to run this. It doesn't magically make things work. If you need root privileges to edit source code you're working on, you have serious problems with your development setup.
Use double-quotes if you want stuff to expand inside them. e.g.
TABLE=$(awk '/sys_call_table/ {print $1}' /boot/System.map-$(uname -r))
sed -i "s/myTABLE/${TABLE}UL/g" my_LKM.c
As karakfa suggested, make sure you try this without -i first. His other suggestion, of expanding $TABLE in an unquoted context, instead of inside double quotes, was terrible, though. You need the entire string to be part of the same sed arg, so just ending the single quotes is bad.
Also, I guess he misread {$TABLE} for ${TABLE}, and simplified to $TABLE. Or actually, from the context of your question, it looks like you want a plain numeric constant, not wrapped in braces. So you should use $TABLE.
Actually, you should use ${TABLE}UL so it's an unsigned-long integer literal. Without those modifiers, an address that didn't fit in the low32 would probably get mangled before the cast to a pointer type was applied.

You have to escape the quotes
sed -i 's/myTABLE/'$TABLE'/g' my_LKM.c
obviously try first NOT in-place (remove -i)

Related

How to read a variable from file, modify and safe it to an other variale

What I want:
There is a file /scripts/backup/config.cfg which contains variables. In my specific case the important ones are:
BACKUPLOCATION=""
ROOTLOCATION="/backup"
Then there is a script /scripts/backup/performBackup.sh
For a specific reason I want a part of the script do the following operations:
read the value of the variable ROOTLOCATION
add a ("/" and) timestamp (Date&Time)
safe the new created value to BACKUPLOCATION (by replacing its current value)
Example
If this is the previous state of the config.cfg:
BACKUPLOCATION="dummy"
ROOTLOCATION="/backup"
After the script ran it should be:
BACKUPLOCATION="/backup/2020-05-02-23-00"
ROOTLOCATION="/backup/"
What I tried
First of all the config file gets "loaded" using
source /scripts/backup/config.cfg
I then tried to use the sed command but the quotes are messing with me. Here is one try (which didn't work):
sed -i 's/BACKUPLOCATION\=.*/BACKUPLOCATION="'$ROOTLOCATION/$(date +%Y-%m-%d-%H-%M)'"/' /scripts/backup/config.cfg
Try this:
source /scripts/backup/config.cfg
sed -i 's|BACKUPLOCATION=.*|BACKUPLOCATION="'"$ROOTLOCATION/$(date +%Y-%m-%d-%H-%M)"'"|' /scripts/backup/config.cfg
The problem with your sed is that you use / as delimiter, which is present in $ROOTLOCATION after expansion, therefore sed fails. I used |, which is usually is not present in filenames. If you ever create a file with |, that sed will fail too! So, "know your data" :)

escape one variable but not the other in awk

I'm facing a problem with awk in Linux. I would like to do make this script work :
awk -v var="$MYVAR" "{gsub(/export OTHER_VAR=\$OTHER_VAR:/, "var")}1" /etc/myfile
The problem here is that I want the variable "var" to be interpreted (it works) and the variable $OTHERVAR not to be interpreted, and this I what I can't manage to do.
In the end, I want to do this:
I have a variable
MYVAR=export OTHER_VAR=\$OTHER_VAR:some_text
I want to replace, in /etc/myfile, the following pattern :
export OTHER_VAR=$OTHER_VAR:/folder/bin by export OTHER_VAR=$OTHER_VAR:some_text:/folder/bin.
I hope I made myself clear ...
Thanks in advance !
Sylvain
test_document='export OTHER_VAR=$OTHER_VAR:whatever'
search_regex='^export OTHER_VAR=[$]OTHER_VAR:'
replace_str='export OTHER_VAR=$OTHER_VAR:some_text:'
awk -v search_regex="$search_regex" \
-v replace_str="$replace_str" \
'{gsub(search_regex, replace_str)} {print}' <<<"$test_document"
...properly emits as output:
export OTHER_VAR=$OTHER_VAR:some_text:whatever
Note some changes:
We're escaping the $ in the regex as [$]. Unlike \$, this is parsed consistently across all quoting contexts: It is explicitly generating a regex character class, rather than having any other potential meaning.
Using single quotes for literal strings ensures that no shell interpolation takes place within them.
Using {print} is a bit easier for readers to understand than a bare 1 in awk.
Excluding variable names with meaning to the OS or shell, use of lower-case characters in variable names is in line with POSIX-specified convention. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, fourth paragraph.

funky file name output from shell/bash?

So, im making a small script to do an entire task for me. The task is to get the output of the dmidecode -Fn into a text file and then take a part of the dmidecode output, in my example, the Address (0xE0000) as the file name of the txt.
My script goes as follows and does work, i have tested it. The only little issue that i have, is that the file name of the txt appears as "? 0xE0000.txt"
My question is, why am i getting a question mark followed by a space in the name?
#!/bin/bash
directory=$(pwd)
name=$(dmidecode|grep -i Address|sed 's/Address://')
inxi -Fn > $directory/"$name".txt
The quotes in the "$name".txt is to avoid an "ambiguous redirect" error i got when running the script.
Update #Just Somebody
root#server:/home/user/Desktop# dmidecode | sed -n 's/Address://p'
0xE0000
root#server:/home/user/Desktop#
Solution
The use of |sed -n 's/^.*Address:.*0x/0x/p' got rid of the "? " in 0xE0000.txt
A big thanks to everyone!
You've got a nonprinting char in there. Try:
dmidecode |grep -i Address|sed 's/Address://'| od -c
to see exactly what you're getting.
UPDATE: comments indicate there's a tab char in there that needs to be cleaned out.
UPDATE 2: the leading tab is before the word Address. Try:
name=$(dmidecode |grep -i Address|sed 's/^.*Address:.*0x/0x/')
or as #just_somebody points out:
name=$(dmidecode|sed -n 's/^.*Address:.*0x/0x/p')
UPDATE 3
This changes the substitution regex to replace
^ (start of line) followed by .* (any characters (including tab!)) followed by Address: followed by .* (any characters (including space!)) followed by 0x (which are always at the beginning of the address since it's in hex)
with
0x (because you want that as part of the result)
If you want to learn more, read about sed regular expressions and substitutions.

Understanding sed

I am trying to understand how
sed 's/\^\[/\o33/g;s/\[1G\[/\[27G\[/' /var/log/boot
worked and what the pieces mean. The man page I read just confused me more and I tried the info sai Id but had no idea how to work it! I'm pretty new to Linux. Debian is my first distro but seemed like a rather logical place to start as it is a root of many others and has been around a while so probably is doing stuff well and fairly standardized. I am running Wheezy 64 bit as fyi if needed.
The sed command is a stream editor, reading its file (or STDIN) for input, applying commands to the input, and presenting the results (if any) to the output (STDOUT).
The general syntax for sed is
sed [OPTIONS] COMMAND FILE
In the shell command you gave:
sed 's/\^\[/\o33/g;s/\[1G\[/\[27G\[/' /var/log/boot
the sed command is s/\^\[/\o33/g;s/\[1G\[/\[27G\[/' and /var/log/boot is the file.
The given sed command is actually two separate commands:
s/\^\[/\o33/g
s/\[1G\[/\[27G\[/
The intent of #1, the s (substitute) command, is to replace all occurrences of '^[' with an octal value of 033 (the ESC character). However, there is a mistake in this sed command. The proper bash syntax for an escaped octal code is \nnn, so the proper way for this sed command to have been written is:
s/\^\[/\033/g
Notice the trailing g after the replacement string? It means to perform a global replacement; without it, only the first occurrence would be changed.
The purpose of #2 is to replace all occurrences of the string \[1G\[ with \[27G\[. However, this command also has a mistake: a trailing g is needed to cause a global replacement. So, this second command needs to be written like this:
s/\[1G\[/\[27G\[/g
Finally, putting all this together, the two sed commands are applied across the contents of the /var/log/boot file, where the output has had all occurrences of ^[ converted into \033, and the strings \[1G\[ have been converted to \[27G\[.

Linux rename function not being used correctly

I'm trying to use the rename command in a Terminal in Ubuntu to append a string to the beginning of some avi file names as follows.
rename -n 's/(\w)\.avi$/String_to_add__$1\.avi/' *.avi
So I expect the following:
String_to_add_MyMovie.avi
Problem is that when I run the command it appends the string to the end of the file name, so I end up with the following:
MyMovie_String_to_add_.avi
I'm not sure if I have the perlexpr syntax wrong or something else. Any insight is appreciated.
UPDATE:
Thanks for the suggestions, I tried the suggestions from alno and plundra and made the following modification:
rename -n 's/(\w+)\.avi$/String_to_add__$1\.avi/' *.avi
But now the file gets the string inserted in the middle of the name as follows:
My_String_to_add_Movie
My apologies though, I neglected to mention that the titles are preceded by 3 numeric values, so the file name nomenclature is {3 numbers}-My_Movie.avi so for example 001-My_Movie.avi. But I didn't think this would make a difference since I'm assuming \w+ matches alphanumeric characters, might the '-' be the issue?
Haven't tried Christian's approach yet, I want to be able to use the rename command, or at least understand why it's not working before I try a different approach.
I don't think rename -n is standard. You could do this:
for i in *.avi; do mv $i String_to_add_$i; done
You're only matching a single character with \w, you want \w+, so the complete line would be:
rename -n 's/(\w+)\.avi$/String_to_add__$1\.avi/' *.avi
Correct version:
rename -n 's/(\w+)\.avi$/String_to_add__$1\.avi/' *.avi
You simply forgot + after \w, so it tried to match only one character.

Resources