I would like to edit a .mk file using Bash.
Inside the file, it looks like this:
SRC_PATHS = src/lib \
src/Application \
src/win \
src/prj
I would like to add a new source, which should look like this:
SRC_PATHS = src/lib \
src/Application \
src/win \
src/prj \
src/New
I am trying a sed command, but cannot add a new line.
Note: the last src path (src/prj) is not always the same.
If ed is available/acceptable.
#!/usr/bin/env bash
ed -s file.mk <<-'EOF'
$t.
-1s/$/ \\/
+s|\(^[[:blank:]]\{1,\}\) \(.\{1,\}\)$|\1 scr/new|
,p
Q
EOF
In-one-line
printf '%s\n' '$t.' '-1s/$/ \\/' '+s|\(^[[:blank:]]*\) \(.*\)$|\1 scr/new|' ,p Q | ed -s file.mk
with a shell variable to store the replacement.
#!/usr/bin/env bash
var='scr/new'
ed -s file.mk <<-EOF
\$t.
-1s/\$/ \\\/
+s|\(^[[:blank:]]\{1,\}\) \(.\{1,\}\)\$|\1 $var|
,p
Q
EOF
Remove the ,p to silence the output to stdout , it is there just to see what is the new outcome of the edited buffer.
Change Q to w if in-place editing is needed
JFYI, both the script and the one-liner are not limited to just bash it should work on any POSIX compliant shell.
With sed how about:
sed -i '$s#.\+#& \\'\\$'\n'' src/New#' file.mk
Result:
SRC_PATHS = src/lib \
src/Application \
src/win \
src/prj \
src/New
Considering the indentation of the input and of the desired result, which is not uniform between the first line and the others, I suspect that it is not important at all. If this is the case, then this sed command might work:
sed -z 's#\n$# \\\nsrc/New\n#' file.mk
where
-z is to treat the file as a single line/stream with embedded \ns
\n$ targets the EOF together with the last \n
the replacement string is \\\nsrc/New\n.
Thanks to all who answered, I tried all your suggestions, and here are the code snippets working and applicable to my needs:
sed -i '/^SRC_PATHS[\t ]*=/{:a;/\\$/{N;ba;};s,$, \\\n\tsrc/New,}' file.mk
there are some instances where there is already a "\" in the file, so I added new code to clean up those lines
sed -i '/^$*.\\/d' file.mk
then to add another path in the EOF:
sed -i '$s#.\+#& \\'\\$'\n'' src/New#' file.mk
Related
I have a big file name lef_list with data in below format:
$PRJ/fp/t/std/tcb/libs/tcb.lb.gz \
$PRJ/mm/T/v/mem_gen/ram/NLDM/ram.lib \
I want to read each line of file and split the lines into two based on pattern '/libs/' or '/NLDM/' in line and replace those with word '/LEF/' and write in a new file.
The output should be like :
$PRJ/fp/t/std/tcb/lef/tcb.lb.gz \
$PRJ/mm/T/v/mem_gen/ram/LEF/ram.lib \
I tried the below code:
while IFS="/libs/" read -r line val
do
echo "$line"/LEF/"$val";
done < lef_list
This code shows error as while expression syntax. command not found.
Someone please suggest any solution.
A simple sed should do the job:
sed -E 's~/(libs|NLDM)/~/LEF/~g' file
$PRJ_WORK_DIR/foundationip/tsmc/tsmc_lvt_stdcells/tcbn16ffcllbwp16p90cpdlvt/LEF/ccs/tcbn16ffcllbwp16p90cpdlvtssgnp0p72vm40c_hm_ccs.lib.gz \
$PRJ_WORK_DIR/mem/tech/TSMC_16FFC/v37/mem_gen/data_new/ram_dp_ulvt_pg_rd_64x44_mux4/LEF/ram_dp_ulvt_pg_rd_64x44_mux4_ssgnp0p72vm40c.lib \
Pattern /(libs|NLDM)/ matches /libs/ or /NLDM/ and replaces that with /LEF/.
Or if you have to use awk only then:
awk '{gsub(/\/(libs|NLDM)\//, "/LEF/")} 1' file
<proxybypass var="proxybypass">xxx.yyy.com|\DDD.yyy.com</proxybypass>
Where DDD is server name as defined ${sname}
unable to pass the variable \ after xxx.yyy.com| when using below code:
in short I have to by pass |\ character in updateflag
updateflag="<proxybypass var=\"proxybypass\">xxx.yyy.com|\"${sname}".yyy.com</proxybypass>"
sed -i ''"$line_number"'a '"$updateflag"'' $xmlval
If it's possible change the updateflag, so that it contains an additional backslash (to escape the one you want to keep):
updateflag="...|\\\\${name}..."
Then:
sed "1a $updateflag" <<< "test"
Gives:
test
|\DDD
I have qmake project with target which generates documentation. Away of qmake in pure bash I would write like this:
PWDMOD=$(echo $PWD | sed -e 's/\//\\\//g') ;
sed -i 's/INPUT = /INPUT = $PWDMOD\/src\/fileset/g' $PWD/Doxyfile
Yep, I need to correct to INPUT variable in Doxyfile while make doc executed. But in my case - in qmake environment, I wrote this:
doc.commands = \
(PWDMOD=$(echo $$PWD | sed -e 's/\//\\\//g') ; \
sed -i 's/INPUT = /INPUT = $$PWDMOD\/src\/fileset/g' $$PWD/Doxyfile ; \
...
and qmake parser sent me error message about first line. Something like "Incorrect using parenthesses in way like this $(...)".
So how I should write such qmake code? Or how can I redirect stdout from first sed command to second to avoid such situation? Thanks.
There is a nice ~= function in qmake that will save you one extra call to sed.
PWDMOD = $$PWD
PWDMOD ~= s,/,\\/,g
doc.commands = sed -i \'s/INPUT = /INPUT = $$PWDMOD\\/src\\/fileset/g\' $$PWD/Doxyfile
I'm trying to use sed to change a variable in the site.js file on my server.
Here is the line: var url = "page.php"; I'm looking to just substitute page.php for whatever.php.
I thought this would be pretty simple and I figured this would work with no issues:
sed -i "s/\url = \".*\"/\url = \"page2.php\"/" /home/site.js
It works okay except instead of getting: var url = "page2.php"; I get: var R1 = "page2.php";
Why is the url value being changed to R1 when I use sed here?
You don't need \ before url.
sed -i -r 's#url\s*=\s*"[^"]+"#url = "page2.php"#' /home/site.js
Extra escaping of " can be eliminated by enclosing sed expression with ' instead of "
It's better to use different separator than / (here #) when the strings themselves may contain /
Try doing this :
sed -i -r 's#(var\s+url\s*=\s*")[^"]+"#\1whatever.php"#' file.js
/ is not mandatory as delimiter, I've picked up # there.
Here's another example: Took me while to figure that you change the / for delimiter and not the / in the directory path.
Use # instead of / for sed delimiter if you have dir path names.
First I tried this:
[root#ip-172-35-24-37 ec2-user]# egrep -q "^(\s*\S+\s+)/dev/shm(\s+\S+\s+\S+)(\s+\S+\s+\S+)(\s*#.*)?\s*$" /etc/fstab && sed -ri "s/^(\s*\S+\s+)/dev/shm(\s+\S+\s+\S+)(\s+\S+\s+\S+)(\s*#.*)?\s*$/\1/dev/shm\2nodev\3\4/" /etc/fstab
And got this error:
sed: -e expression #1, char 20: unknown option to `s'
So then I used # for the sed delimiter instead of /:
[root#ip-172-35-24-37 ec2-user]# egrep -q "^(\s*\S+\s+)/dev/shm(\s+\S+\s+\S+)(\s+\S+\s+\S+)(\s*#.*)?\s*$" /etc/fstab && sed -ri "s#^(\s*\S+\s+)/dev/shm(\s+\S+\s+\S+)(\s+\S+\s+\S+)(\s*#.*)?\s*$#\1/dev/shm\2nodev\3\4##" /etc/fstab
[root#ip-172-35-24-37 ec2-user]#
And it worked.
You can use something else besides # for a delimiter like ! or ? or %. Just don't use / if you have dir paths.
I adapted Jan Goyvaerts's e-mail regex to a bash function to be used in pipes to anonymize e-mail addresses:
function remove_emails {
sed -r "s|\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b|email.address#removed.com|gI";
}
which I'm using in a bash pipe:
mysqldump \
-uuser \
-ppass \
db_name \
| remove_emails \
| gzip -c \
| cat \
> tmp.sql.gz
works fine but now, I'd like to have different random e-mails, I'd be satisfied with:
email.address1#removed.com
email.address2#removed.com
or
eiyyzhupzftrvjwehbqp#removed.com
kwmbrshzmxqlrqatqpff#removed.com
or anything that differs and is unique
I'm quite comfortable with bash but using counters, process substitution and so fails as sed is invoked only once, so
sed "s,sth,$(echo $RANDOM),g"
and similar won't work,
Is there anything to generate random stuff or counters in sed itself?
This might work for you (GNU sed):
<<<'Here is a random number.' sed 's/random number/& $RANDOM/;s/.*/echo "&"/e'
or if you prefer:
<<<'Here is a random number.' sed 's/random number/& $RANDOM/;s/.*/echo "&"/' | sh
I experimented with potong's correct answer and found a way to implement an iterator which answers the other part of my question:
remove_emails() {
sed -r 's|\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b|test$(( iterator++ ))#example.com|gI;s|.*|echo "&"|' | bash
}
iterator=0
test_data='some.e.mail.address.#domain.com\nsome.other#email.co.uk\nwhatever#man.biz\nsed#sed.com\n'
echo -e "before:\n${test_data}"
echo -e "after: \n${test_data}" | remove_emails
You could do it by repeatedly invoking sed in a while loop as shown below:
remove_emails() {
while read line
do
sed -r "s|\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b|email.address${RANDOM}#removed.com|gI" <<< "$line"
done
}