Use $HOSTNAME in sed substitution [duplicate] - linux

This question already has answers here:
Replace a string in shell script using a variable
(12 answers)
Closed 8 years ago.
I want to use my Centos VM's $HOSTNAME inside a call to sed like so:
sed -i 's/Apache 2 Test Page/$HOSTNAME Test Page/g' /var/www/error/noindex.html
But this just replaces Apache 2 Test Page with $HOSTNAME Test Page... I know I can do it manually, but I've got a good reason for wishing to do it this way.
FWIW, I'm actually doing this inside a bash script that gets called from a Vagrantfile, which in turn provisions multiple VM's (each with Apache2 installed), so that I can test-out the load-balancing capabilities of HAProxy. so I just want a simple way to differentiate between my 3 web-servers, and II figure that modifying the default Apache page is the easiest way to do that.

Enclose sed's s/// with double quotes instead of single quotes for variable expansion.
sed -i "s/Apache 2 Test Page/$HOSTNAME Test Page/g" /var/www/error/noindex.html
Example:
$ echo 'foo Apache 2 Test Page bar' | sed "s/Apache 2 Test Page/$HOSTNAME Test Page/g"
foo avinash-Lenovo-IdeaPad-Z500 Test Page bar

Related

pass string with spaces to gcc [duplicate]

This question already has answers here:
How can I store a command in a variable in a shell script?
(12 answers)
Closed 4 years ago.
These work as advertised:
grep -ir 'hello world' .
grep -ir hello\ world .
These don't:
argumentString1="-ir 'hello world'"
argumentString2="-ir hello\\ world"
grep $argumentString1 .
grep $argumentString2 .
Despite 'hello world' being enclosed by quotes in the second example, grep interprets 'hello (and hello\) as one argument and world' (and world) as another, which means that, in this case, 'hello will be the search pattern and world' will be the search path.
Again, this only happens when the arguments are expanded from the argumentString variables. grep properly interprets 'hello world' (and hello\ world) as a single argument in the first example.
Can anyone explain why this is? Is there a proper way to expand a string variable that will preserve the syntax of each character such that it is correctly interpreted by shell commands?
Why
When the string is expanded, it is split into words, but it is not re-evaluated to find special characters such as quotes or dollar signs or ... This is the way the shell has 'always' behaved, since the Bourne shell back in 1978 or thereabouts.
Fix
In bash, use an array to hold the arguments:
argumentArray=(-ir 'hello world')
grep "${argumentArray[#]}" .
Or, if brave/foolhardy, use eval:
argumentString="-ir 'hello world'"
eval "grep $argumentString ."
On the other hand, discretion is often the better part of valour, and working with eval is a place where discretion is better than bravery. If you are not completely in control of the string that is eval'd (if there's any user input in the command string that has not been rigorously validated), then you are opening yourself to potentially serious problems.
Note that the sequence of expansions for Bash is described in Shell Expansions in the GNU Bash manual. Note in particular sections 3.5.3 Shell Parameter Expansion, 3.5.7 Word Splitting, and 3.5.9 Quote Removal.
When you put quote characters into variables, they just become plain literals (see http://mywiki.wooledge.org/BashFAQ/050; thanks #tripleee for pointing out this link)
Instead, try using an array to pass your arguments:
argumentString=(-ir 'hello world')
grep "${argumentString[#]}" .
In looking at this and related questions, I'm surprised that no one brought up using an explicit subshell. For bash, and other modern shells, you can execute a command line explicitly. In bash, it requires the -c option.
argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."
Works exactly as original questioner desired. There are two restrictions to this technique:
You can only use single quotes within the command or argument strings.
Only exported environment variables will be available to the command
Also, this technique handles redirection and piping, and other shellisms work as well. You also can use bash internal commands as well as any other command that works at the command line, because you are essentially asking a subshell bash to interpret it directly as a command line. Here's a more complex example, a somewhat gratuitously complex ls -l variant.
cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"
I have built command processors both this way and with parameter arrays. Generally, this way is much easier to write and debug, and it's trivial to echo the command you are executing. OTOH, param arrays work nicely when you really do have abstract arrays of parameters, as opposed to just wanting a simple command variant.

split a string and get the third last field from it?

I am running my shell script on multiple machines and all those machines can be in different datacenters.
If machine is in abc datacenter, then I don't want to sleep at all and move forward to next line in the shell script.
If machine is in def datacenter, then I want to sleep for 30 minutes and after that I will move to the next line in the shell script.
If machine is in pqr datacenter, then I want to sleep for 60 minutes and after that I will move to the next line in the shell script.
My machine name is like this and it will be always and as you can see, datacenter name is always before .host.com and it will be like this only.
machineA.abc.host.com
machineB.def.host.com
machineC.pqr.host.com
machinef-12341.testra.abc.host.com
.....
In my below shell script, I already have machine name stored in HOSTNAME variable so how can I extract the datacenter name from that in shell script and apply above conditions? I need to extract datacenter name which is just before .host.com so I need to do start from at the end?
#!/usr/bin/env bash
HOSTNAME=$hostname
.....
// I want to execute this line after the above if/else if logic
echo "Hello World"
What is the best way to do this? I can split the lines into variables but how to get relevant portion which I need and then apply if/elseif logic here?
Two different solutions in a testloop:
hosts="machineA.abc.host.com machineB.def.host.com machineC.pqr.host.com machinef-12341.testra.abc.host.com"
for testhost in ${hosts}; do
echo "sed ${testhost}: $(sed 's/.*\.\([^.]*\).host.com$/\1/' <<< "${testhost}")"
echo "cut ${testhost}: $(rev <<< "${testhost}"|cut -d"." -f3 | rev)"
done

How to parse xml sibling element value with SED [duplicate]

This question already has answers here:
How to parse XML using shellscript? [duplicate]
(10 answers)
Closed 7 years ago.
I have a xml file which contains following text:
<Cluster>
<Name>CLS_20</Name>
<JMXUserName>admin</JMXUserName>
<JMXPassword>mypwd</JMXPassword>
</Cluster>
<Server>
<Name>Server_20</Name>
<IpAddress>a.b.c.d</IpAddress>
<Port>1234</Port>
</Server>
<Server>
<Name>Server_21</Name>
<IpAddress>e.f.g.h</IpAddress>
<Port>1234</Port>
</Server>
I have the IP address of the server (a.b.c.d)
I want to retrieve name of the server (Server_20)
How can this be achieved with SED
Or with any other linux command.
Please help.
Sed is to use with caution on xml due to lot of variation in structure but based on this sample
sed -n '
/<Server>/ h
/<Server>/,\#</Server># {
H
\#</Server># {
x
s#.*<Name>\([^<]*\)<.*IpAddress>a.b.c.d<.*#\1#p
}
}' YourFile
Principle:
don't print unless explicitly asked
load section Server in buffer
when reaching end of section
change the whole by keeping only name attribut if the ipadresse value is the same as a.b.c and in this case print the result
Do not do this with sed; it will break horribly when benign formatting changes happen to the XML.
Use a proper XML-parsing tool. For example with xmlstarlet:
xmlstarlet sel -t -c '//Server[IpAddress="a.b.c.d"]/Name/node()' -n filename.xml
or with xmllint:
xmllint --xpath '//Server[IpAddress="a.b.c.d"]/Name/node()' filename.xml
or with old versions of xmllint that don't yet understand --xpath (if you are tempted to use this I encourage you to look at other tools):
echo 'cat //Server[IpAddress="a.b.c.d"]/Name/node()' | xmllint --shell filename.xml | sed '1d;$d'
or with the xpath utility from the XML::XPath Perl library:
xpath -q -e '//Server[IpAddress="a.b.c.d"]/Name/node()' filename.xml
...or with any of three dozen (dozen) other XML tools.
The heart of this is the XPath expression //Server[IpAddress="a.b.c.d"]/Name/node(). This consists of:
//Server refers to a Server node anywhere in the document
//Server/Name refers to a Name node that is the child of such a Server node
//Server/Name/node() refers to the contents of such a Name node
//Server[IpAddress="a.b.c.d"] refers to a server node that satisfies the condition IpAddress="a.b.c.d", which means that it has a child IpAddress node that contains a.b.c.d
Putting all that together, //Server[IpAddress="a.b.c.d"]/Name/node() refers to the contents of a Name node that is the child of a Server node anywhere in the document that has an IpAddress child node that contains a.b.c.d.

Passing quotes and other special characters literally through bash and ssh

I am trying to run an SSH command that will invoke a script on a remote machine that writes some Lua code to a file.
I have this script command that executes under bash:
ssh bob writelua.sh '{version=1,{["foo"]=17}}'
And writelua.sh looks like this:
echo "return $1" > bar.lua
The end result, however, is that bar.lua has the content:
return version=1
I had thought that single quotes prevented all interpretation. How can I edit the scripts and escaping to pass the raw Lua code through unharmed?
The single quotes prevent interpretation on the local host. The remote host sees the command line
writelua.sh {version=1,{["foo"]=17}}
which is subject to brace expansion. You need a second set of quotes so that the first set of single quotes is passed through to the remote host.
ssh bob writelua.sh "'{version=1,{[\"foo\"]=17}}'"
As you can see, the quotes start to get unwieldy. A better solution is to simply copy a script containing
writelua.sh '{version=1,{["foo"]=17}}'
to the remote host and execute that remotely.
An example using the $'...' quotes:
ssh bob writelua.sh $'{version=1,{[\'foo\']=17}}'
Use heredoc and avoid all the excessive quoting:
ssh -T bob << \EOF
writelua.sh '{version=1,{["foo"]=17}}'
EOF
This will send raw script to remote host and it will get interpreted on the remote host itself.
When it gets too complex, particularly with lots of escaping, I prefer generating the command on a temporary script and execute it locally or remotely via SSH as required.
But there's an alternative: using echo to store the command in a variable and taking advantage of three things:
Single quotes don't do variable expansion and allow double quotes, so you can include something like "$myvar" without escaping $ or "
Double quotes allow variable expansion and single quotes, which means you can include something like animals='all'; echo love $animals to have $animals replaced by its value, and without escaping the '
Strings of both types, i.e. enclosed by single quotes or double quotes, can be concatenated simply by putting them together.
As an example, if I want something like this executed on a remote machine:
source /my-env.sh; perl -MMYLIB::DB -e 'my $t=db_list("name", 1553786458); print "#$t"'
But instead of 1553786458 I want to pass the value from a local variable:
now=`date +%s`
We could have this:
get_list=`echo 'source /my-env.sh; perl -MMYLIB::DB -e' "'my " '$t=db_list("name", ' "$now" '); print "#$t"' "'"`
You can see that single and double quotes are alternated, so we din't have to do any escaping! They don't need to be separated by spaces, but it improves readability and won't affect the result in this case.
And now we can execute:
ssh user#host $get_list
There's still no guarantee that this approach will always work, so once you've built your command, the safest bet would be to copy it over in a file.
If you can use Perl...
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new("bob");
$ssh->system('writelua.sh', '{version=1,{["foo"]=17}}')
or die $ssh->error;
Net::OpenSSH takes care of quoting everything for you.

replace a line in linux file containing special characters

Here is an extract from a script showing the variables for the script
PathToPiconPNG="/var/OscamSrvidPicon/picon/19.2E/"
PathToOscamSrvid="/var/OscamSrvidPicon/picon/19.2E/oscam.srvid"
PathToPiconTPL="/var/OscamSrvidPicon/oscam_picons/"
PathToTmp="/tmp/"
I want to run this script numerous times replacing (for example) this line:
PathToPiconPNG="/var/OscamSrvidPicon/picon/19.2E/"
with this lines
PathToPiconPNG="/var/OscamSrvidPicon/picon/28.2E/"
I have tried using sed (I know this example is wrong but you might get what im trying to achieve)
sed 's/{PathToPiconPNG="/var/OscamSrvidPicon/picon/19.2E/"}/{PathToPiconPNG="/var/OscamSrvidPicon/picon/28.2E/"}/g' filename.txt > newfilenam.txt
If that is not possible, is there any way that I can set the variable externally from another script
sed -E 's/picon\/.+\//picon\/28.2E\//' filename.txt > newfilenam.txt

Resources