This question already has answers here:
I just assigned a variable, but echo $variable shows something else
(7 answers)
Closed 7 years ago.
I have a shell command spurious ports which returns the following data...
Service Host Port Browser link
spurious-elasticache-docker 192.168.59.103 32794 -
spurious-sqs sqs.spurious.localhost 32789 http://sqs.spurious.localhost:32789
spurious-s3 s3.spurious.localhost 32790 http://s3.spurious.localhost:32790
spurious-elasticache 192.168.59.103 32793 -
spurious-dynamo dynamodb.spurious.localhost 32791 http://dynamodb.spurious.localhost:32791
spurious-browser browser.spurious.localhost 32795 http://browser.spurious.localhost:32795
spurious-memcached 192.168.59.103 32792 -
If I want to get the port number for the "dynamo" service then the following shell script works...
lines=`spurious ports`; echo $lines | grep "dynamo" | awk '{print $3}'
This retuns 32791
But if I try and run this from inside a Makefile, I instead get the entire spurious ports output (all on one line?):
foobar:
$(eval port:=$(shell lines=`spurious ports`; echo $$lines | grep 'dynamo' | awk '{print $3}'))
#echo $(port)
I've also tried moving the commands into a shell script file:
#!/bin/sh
lines=`spurious ports`; echo $lines | grep "dynamo" | awk '{print $3}'
And inside the Makefile used:
PORT:=$(shell ./scripts/spurious-ports.sh)
bing:
#echo $(PORT)
But this doesn't work either.
This seems quotes issue. When you variable with multiple lines you need to put that variable in double quotes to retain the /n at end of line.
for E.g.
VAR="1
2
3
4"
echo $VAR ## here it will print "1 2 3 4"
but echo "$VAR" will print
1
2
3
4
I Think this will solve your problem. For more details read about quotes in shell.
Related
I want to get version number of tomcat.tar.gz file like this
read -p echo " Enter Version (8 or 9)" version
version8=$(printf -- '%s\n' * | grep -oP 'apache-tomcat-$version\K.*(?=\.tar\.gz)')
version9=$(printf -- '%s\n' * | grep -oP 'apache-tomcat-$version\K.*(?=\.tar\.gz)')
echo $version8 #or better $version${version}, but that doesn't work, too
depending on which version the user entered, I will receive the version number from the gz-file in the current folder.
Example:
in my folder are two tar.gz
apache-tomcat-8.5.78.tar.gz
apache-tomcat-9.0.56.tar.gz
Starting the script:
Enter Version (8 or 9): 8
output should be: 8.5.78
With the above code I am getting nothing. What's wrong with it? I suspect it is due to the variable (version) within a variable (version8). How is it syntactically correct?
I have this working with awk if you are interested, as follows:
read -p "Enter Version (8 or 9) " version
ls *.gz | awk "{split(\$0,a,\"-\"); split(a[3],b,\".\"); split(a[3],c,\".tar\"); if (b[1] == \"$version\") {print c[1]}}"
I have a script running to use output from commands that I run using a string from the file that I want to update.
for CLIENT in `cat /home/"$ID"/file.txt | awk '{print $3}'`
do
sed "/$CLIENT/ s/$/ $(sudo bpgetconfig -g $CLIENT -L | grep -i "version name")/" /home/"$ID"/file.txt >> /home/"$ID"/updated_file.txt
done
The output prints out the entire file once for each line with the matching line in question updated.
How do I sort it so that it only sends the matching line to the new file.
The input file contains lines similar to below:
"Host OS" "OS Version" "Hostname"
I want to run a script that will use the hostname to run a command and grab details about an application on the host and then print only the application version to the end of the line with the host in it:
"Host OS" "OS Version" "Hostname" "Application Version
What you're doing is very fragile (e.g. it'll break if the string represented by $CLIENT appears on other lines or multiple times on 1 line or as substrings or contains regexp metachars or...) and inefficient (you're reading file.txt one per iteration of the loop instead of once total) and employing anti-patterns (e.g. using a for loop to read lines of input, plus the UUOC, plus deprecated backticks, etc.)
Instead, let's say the command you wanted to run was printf '%s' 'the_third_string' | wc -c to replace each third string with the count of its characters. Then you'd do:
while read -r a b c rest; do
printf '%s %s %s %s\n' "$a" "$b" "$(printf '%s' "$c" | wc -c)" "$rest"
done < file
or if you had more to do and so it was worth using awk:
awk '{
cmd = "printf \047%s\047 \047" $3 "\047 | wc -c"
if ( (cmd | getline line) > 0 ) {
$3 = line
}
close(cmd)
print
}' file
For example given this input (courtesy of Rabbie Burns):
When chapman billies leave the street,
And drouthy neibors, neibors, meet;
As market days are wearing late,
And folk begin to tak the gate,
While we sit bousing at the nappy,
An' getting fou and unco happy,
We think na on the lang Scots miles,
The mosses, waters, slaps and stiles,
That lie between us and our hame,
Where sits our sulky, sullen dame,
Gathering her brows like gathering storm,
Nursing her wrath to keep it warm.
We get:
$ awk '{cmd="printf \047%s\047 \047"$3"\047 | wc -c"; if ( (cmd | getline line) > 0 ) $3=line; close(cmd)} 1' file
When chapman 7 leave the street,
And drouthy 8 neibors, meet;
As market 4 are wearing late,
And folk 5 to tak the gate,
While we 3 bousing at the nappy,
An' getting 3 and unco happy,
We think 2 on the lang Scots miles,
The mosses, 7 slaps and stiles,
That lie 7 us and our hame,
Where sits 3 sulky, sullen dame,
Gathering her 5 like gathering storm,
Nursing her 5 to keep it warm.
The immediate answer is to use sed -n to not print every line by default, and add a p command where you do want to print. But running sed in a loop is nearly always the wrong thing to do.
The following avoids the useless cat, the don't read lines with for antipattern, the obsolescent backticks, and the loop; but without knowledge of what your files look like, it's rather speculative. In particular, does command need to run for every match separately?
file=/home/"$ID"/file.txt
pat=$(awk '{ printf "\\|$3"}' "$file")
sed -n "/${pat#\\|}/ s/$/ $(command)/p' "$file" >> /home/"$ID"/updated_file.txt
The main beef here is collecting all the patterns we want to match into a single regex, and then running sed only once.
If command needs to be run uniquely for each line, this will not work out of the box. Maybe then turn back to a loop after all. If your task is actually to just run a command for each line in the file, try
while read -r line; do
# set -- $line
# client=$3
printf "%s " "$line"
command
done <file >>new_file
I included but commented out commands to extract the third field into $client before you run command.
(Your private variables should not have all-uppercase names; those are reserved for system variables.)
Perhaps in fact this is all you need:
while read -r os osver host; do
printf "%s " "$os" "$osver" "$host"
command "$host" something something
done </home/"$ID"/file.txt >/home/"$ID"/updated_file.txt
This assumes that the output of command is a well-formed single line of output with a final newline.
This might work for you (GNU sed, bash/dash):
echo "command () { expr length \"\$1\"; }" >> funlib
sed -E 's/^((\S+\s){2})(\S+)(.*)/. .\/funlib; echo "\1$(command "\3")\4"/e' file
As an example of a command, I create a function called command and append it to a file funlib in the current directory.
The sed invocation, sources the funlib and runs the command function in the RHS of the substitution command within an interpolation of string, displayed by the echo command which is made possible by the evaluation flag e.
N.B. The evaluation uses the dash shell or whatever the /bin/sh is symlinked to.
This question already has answers here:
When to wrap quotes around a shell variable?
(5 answers)
Closed 4 years ago.
I have a file which was created by another scripts with file names. Only thing is that it contains * for similar file names. now i want to iterate a for loop on this file to execute a few operation but i want to do it on different servers for that i simply need exact same name from that file. For this, i used grep and passed it to 'for' loop for one by one execution. But instead before going for the loop it actually executes it on current server itself and gives wrong output. I don't know what i'm missing.
# node_name=auxiliary;grep -F $node_name /opt/log_automation/ME_log_sheet.csv|awk -F "," '{print $2}'
/opt/tpa/logs/install_SPS.*.log
/opt/tpa/logs/localization.log
/opt/tpa/logs/SPI.*.log
/opt/tpa/logs/TPA_system.log
#
This seems to be working as expected as i want * to be present in the name. Now if i put it in a variable or pass it to the loop it expands and gives actual present file which i don't want. I want it to return simply the name with * on it.
# node_name=auxiliary;myvar=$(grep -F $node_name /opt/log_automation/ME_log_sheet.csv|awk -F "," '{print $2}');echo $myvar
/opt/tpa/logs/install_SPS.2019.02.09-12:12.log /opt/tpa/logs/localization.log /opt/tpa/logs/SPI.2019.02.09-12:10.log /opt/tpa/logs/TPA_system.log
# node_name=auxiliary;for i in $(grep -F $node_name /opt/log_automation/ME_log_sheet.csv|awk -F "," '{print $2}');do echo $i;done
/opt/tpa/logs/install_SPS.2019.02.09-12:12.log
/opt/tpa/logs/localization.log
/opt/tpa/logs/SPI.2019.02.09-12:10.log
/opt/tpa/logs/TPA_system.log
Please help me resolve this. What am i doing wrong?
P.S.: '-F' with grep i added after a few changes. It wasn't working either way.
Quote all variables:
node_name=auxiliary
for i in "$(grep -F $node_name /opt/log_automation/ME_log_sheet.csv|awk -F "," '{print $2}')"
do
echo "$i"
done
I think you should be using set -f to disable filename expansion : https://unix.stackexchange.com/questions/333867/what-does-set-f-in-korn-shell-do
However as triplee suggest, using such characters (eg asterisk) in filenames is a very bad practice..
This question already has answers here:
How to remove a newline from a string in Bash
(11 answers)
Closed 4 years ago.
I have the following:
VERSION=$(curl -Is https://qa.me.com.br | sed -n '/^x-powered-by:/Ip' | sed '/x-powered-by:/I s/x-powered-by: //Ig')
Expected variable result (but it has one more character that broke my result):
MEWeb - QA - 267_4_2548
After, I'm showing by the following:
echo "##teamcity[progressMessage 'Version is $VERSION']"
Expected (without '*'):
*##teamcity[progressMessage 'Version is MEWeb - QA - 267_4_2548']
Actual:
']##teamcity[progressMessage 'Version is MEWeb - QA - 267_4_2548
I don't know what is breaking my result.
Thanks for help and sorry for my english!
Add
| tr -d '\r'
to the end of the curl command (just before the ")").
The response has a carriage return.
When you get the VERSION, put it in a file.
echo $VERSION > test.txt
Now, to see the hidden characters, use:
cat -v test.txt
You'll see:
MEWeb - QA - 267_4_2548**^M**
You need to handle that character, which is causing the trouble.
This question already has answers here:
Iterate over two arrays simultaneously in bash
(6 answers)
Closed 6 years ago.
in my question i learn to use loops and with you help I final a installation script for multible instance of a software. thank you very much :)
now I try to automatic setup the configuration files by using sed. For this i need multible variables in a loop.
I read from the system the IP-Adresses and the hostnames for the IP's (PTR)
IPADDR=`ifconfig | awk '{print $2}' | egrep -o '([0-9]+\.){3}[0-9]+'|grep -v 127.0.0.1`
for ipaddr in ${IPADDR[#]}; do echo $ipaddr; done
for iphost in ${IPADDR[#]}; do host $iphost |grep pointer | awk '{print $NF}' RS='.\n'; done
my Script know, there ar 3 IP's, know the IP-Addresses and the Hostnames.
the numbers of IP (3) are now my 001 002 003. this running well.
if I like to edit the config files with sed, I need the 3 variable to do this.
command anyname-001 -some -parameter in my case is a copy to a path. my path is now
/etc/anyname-001, /etc/anyname-003 and /etc/anyname-003
by using sed I need also the 3 IP-Addresses and the 3 hostnames.
sed -i 's/IPADDR/'${ipaddr}'/g' /etc/anyname-${number}/config.cfg
sed -i 's/HOSTNAME/'${hostname}'/g' /etc/anyname-${number}/config.cfg
how can I bring my loop to this with all variables on same time. T try many things. I found nested loops but it not work
001 >> IP:a.a.a.a >> hostname aaa.aaa.aa
002 >> IP:b.b.b.b >> hostname bbb.bbb.bb
003 >> IP:c.c.c.c >> hostname ccc.ccc.cc
Thank you
Assuming the length of both of your arrays HOSTNAME and IPADDR are the same, then you can loop over their elements via indexes in one run.
The length of an array is calculated by using the '#' in the array variable, for example as:
echo ${#HOSTNAME[#]}
So, overall your code would look like something:
count=${#HOSTNAME[#]}
for (( i=0; i<${count}; i++ ));
do
echo ${HOSTNAME[$i]};
echo ${IPADDR[$i]};
((j=i+1));
sed -i 's/IPADDR/'${IPADDR[$i]}'/g' /etc/anyname-${j}/config.cfg
sed -i 's/HOSTNAME/'${HOSTNAME[$i]}'/g' /etc/anyname-${j}/config.cfg
done