Bash shell programming echo without jumping to next line [duplicate] - bash

This question already has answers here:
"echo -n" prints "-n"
(11 answers)
Closed 6 years ago.
#!/bin/bash
echo "Enter process name"
read process
if pgrep "$process" > /dev/null
then
echo 0 $(awk '/Rss/ {print "+", $2}' /proc/`pidof $process`/smaps) | bc;
echo "Kb"
else
echo "Process $process not running"
fi
The output of the code above is
41250
Kb
and I need the "Kb" output in the same line as the number like this
41250 Kb

You can use echo -n to output data without a trailing newline but, for this particular case, awk is perfectly capable of doing arithmetic and formatting on its own, without involving bc or unnecessary sub-processes:
awk '/Rss/ {sum += $2} END {print sum" Kb"}' /proc/`pidof $process`/smaps
You can see that in the following transcript which adds up the two Rss figures 75 and 11 to get 86:
pax> printf Rss 75\nCss 22\nRss 11\n' | awk '/Rss/ {sum += $2} END {print sum" Kb"}'
86 Kb

Use the -n Flag.
Example:
echo -n 'I do not want a new line,'
echo ' because there is something else here.'
Use $ man echo to get more information.

The newline after 41250 was actually from bc, not echo.
Capturing output of bc in a variable removes trailing newlines (ref).
#!/bin/bash
kbCount=$( echo "1+2" | bc)
echo $kbCount "Kb"
Now you should be able to fix it :)

Related

How to use multiple variable in for loop in sh? [duplicate]

This question already has answers here:
How do I pipe a file line by line into multiple read variables?
(3 answers)
Closed 18 days ago.
I want to use multiple variable in for loop at once in sh.
I have a query like this:
top -n 1 -b -c| awk -vOFS="\t" '{print $1,$2,$9}'
I know i use for loop in bash like this:
for i in {2..10}
do
echo "output: $i"
done
what i want to try is:
for x y z in $(top -n 1 -b -c| awk -vOFS="\t" {print $1,$2,$9}')
do
echo "output: $x $y $z"
done
Pipe to a while read loop:
top -n 1 -b -c| awk -vOFS="\t" '{print $1,$2,$9}' | while IFS=$'\t' read -r x y z
do
echo "output: $x $y $z"
done

Remove trailing `,` from bash output [duplicate]

This question already has answers here:
How can I join elements of a Bash array into a delimited string?
(34 answers)
Closed 4 years ago.
I have a script that parses a command:
while read line; do
# The third token is either IP or protocol name with '['
token=`echo $line | awk '{print $3}'`
last_char_idx=$((${#token}-1))
last_char=${token:$last_char_idx:1}
# Case 1: It is the protocol name
if [[ "$last_char" = "[" ]]; then
# This is a protocol. Therefore, port is token 4
port=`echo $line | awk '{print $4}'`
# Shave off the last character
port=${port::-1}
else
# token is ip:port. Awk out the port
port=`echo $token | awk -F: '{print $2}'`
fi
PORTS+=("$port")
done < <($COMMAND | egrep "^TCP open")
for p in "${PORTS[#]}"; do
echo -n "$p, "
done
This prints out ports like:
80,443,8080,
The problem is that trailing slash ,
How can I get the last port to not have a trailing , in the output ?
Thanks
${array[*]} uses the first character in IFS to join elements.
IFS=,
echo "${PORTS[*]}"
If you don't want to change IFS, you can instead use:
printf -v ports_str '%s,' "${PORTS[#]}"
echo "${ports_str%,}"
...or, simplified from a suggestion by Stefan Hamcke:
printf '%s' "${PORTS[0]}"; printf ',%s' "${PORTS[#]:1}"
...changing the echo to printf '%s' "${ports_str%,}" if you don't want a trailing newline after the last port. (echo -n is not recommended; see discussion in the APPLICATION USAGE of the POSIX spec for echo).
how about
$ echo "${ports[#]}" | tr ' ' ','
Why not simply:
( for p in "${PORTS[#]}"; do
echo -n "$p, "
done ) | sed -e 's/,$//'

Unexpected behaviour with awk exit

I have next code:
process_mem() {
used=`sed -n -e '/^Cpu(s):/p' $temp_data_file | awk '{print $2}' | sed 's/\%us,//'`
idle=`sed -n -e '/^Cpu(s):/p' $temp_data_file | awk '{print $5}' | sed 's/\%id,//'`
awk -v used=$used \
-v custom_cpu_thres=$custom_cpu_thres \
'{
if(used>custom_cpu_thres){
exit 1
}else{
exit 0
}
}'
return=$?
echo $return
if [[ $return -eq 1 ]]; then
echo $server_name"- High CPU Usage (Used:"$used".Idle:"$idle"). "
out=1
else
echo $server_name"- Normal CPU Usage (Used:"$used".Idle:"$idle"). "
fi
}
while IFS='' read -r line || [[ -n "$line" ]]; do
server_name=`echo $line | awk '{print $1}'`
custom_cpu_thres=`echo $line | awk '{print $3}'`
if [ "$custom_cpu_thres" = "-" ]; then
custom_cpu_thres=$def_cpu_thres
fi
expect -f "$EXPECT_SCRIPT" "$command" >/dev/null 2>&1
result=$?
if [[ $result -eq 0 ]]; then
process_mem
else
echo $server_name"- Error in Expect Script. "
out=1
fi
echo $server_name
done < $conf_file
exit $out
The problem is that read bash loop should be executed 4 times (one per line readed). However, if I write the awk code with an exit inside, read bash loop exits after first loop.
Why is this happening? In my opinion exit code in awk code shouldn't affect bash script..
Regards.
I believe the statement you make is false.
You stated:
The problem is that read bash loop should be executed 4 times (one per line read). However, if I write the awk code with an exit inside, read bash loop exits after the first loop.
I do not believe that the script exits after the first loop, but is stuck in the first loop. The reason I make this statement is that your awk script is flawed. The way you wrote it is :
awk -v used=$used -v custom_cpu_thres=$custom_cpu_thres \
'{ if(used>custom_cpu_thres){ exit 1 }
else{ exit 0 } }'
The problem here is that Awk did not get an input file. If no input file is proved to awk, it is reading stdin (similar to processing a pipe or keyboard input). Since no information is sent to stdin (unless you pressed a couple of keys and accidentally hit Enter) the script will not move forward and Awk is awaiting input.
The standard input shall be used only if no file operands are specified, or if a file operand is '-', or if a progfile option-argument is '-'; see the INPUT FILES section. If the awk program contains no actions and no patterns, but is otherwise a valid awk program, standard input and any file operands shall not be read and awk shall exit with a return status of zero.
source : Awk POSIX Standard
The following bash-line demonstrates the above statement:
$ while true; do awk '{print "woot!"; exit }'; done
Only when you press some keys followed by Enter, the word "woot!" is printed on the screen!
How to solve your problem:
The easiest way to solve your problem using Awk is by making use of the BEGIN block. This block is executed before it reads any input line (or stdin). If you tell Awk to exit in a begin block, it will terminate Awk without reading any input. Thus:
awk -v used=$used -v custom_cpu_thres=$custom_cpu_thres \
'BEGIN{ if(used>custom_cpu_thres){ exit 1 }
else{ exit 0 } }'
or shorter
awk -v used=$used -v custom_cpu_thres=$custom_cpu_thres \
'BEGIN{ exit (used>custom_cpu_thres) }
However, Awk is a bit of an overkill here. A simple bash test would suffice:
[[ "$used" -le "$custom_cpu_thres" ]]
result=$?
or
(( used <= custom_cpu_thres ))
result=$?

How to add multiple line of output one by one to a variable in Bash?

This might be a very basic question but I was not able to find solution. I have a script:
If I run w | awk '{print $1}' in command line in my server I get:
f931
smk591
sc271
bx972
gaw844
mbihk988
laid640
smk59
ycc951
Now I need to use this list in my bash script one by one and manipulate some operation on them. I need to check their group and print those are in specific group. The command to check their group is id username. How can I save them or iterate through them one by one in a loop.
what I have so far is
tmp=$(w | awk '{print $1})
But it only return first record! Appreciate any help.
Populate an array with the output of the command:
$ tmp=( $(printf "a\nb\nc\n") )
$ echo "${tmp[0]}"
a
$ echo "${tmp[1]}"
b
$ echo "${tmp[2]}"
c
Replace the printf with your command (i.e. tmp=( $(w | awk '{print $1}') )) and man bash for how to work with bash arrays.
For a lengthier, more robust and complete example:
$ cat ./tstarrays.sh
# saving multi-line awk output in a bash array, one element per line
# See http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ for
# more operations you can perform on an array and its elements.
oSET="$-"; set -f # save original set flags and turn off globbing
oIFS="$IFS"; IFS=$'\n' # save original IFS and make IFS a newline
array=( $(
awk 'BEGIN{
print "the quick brown"
print " fox jumped\tover\tthe"
print "lazy dogs back "
}'
) )
IFS="$oIFS" # restore original IFS value
set +f -$oSET # restore original set flags
for (( i=0; i < ${#array[#]}; i++ ));
do
printf "array[%d] of length=%d: \"%s\"\n" "$i" "${#array[$i]}" "${array[$i]}"
done
printf -- "----------\n"
printf -- "array[#]=\n\"%s\"\n" "${array[#]}"
printf -- "----------\n"
printf -- "array[*]=\n\"%s\"\n" "${array[*]}"
.
$ ./tstarrays.sh
array[0] of length=22: "the quick brown"
array[1] of length=23: " fox jumped over the"
array[2] of length=21: "lazy dogs back "
----------
array[#]=
"the quick brown"
array[#]=
" fox jumped over the"
array[#]=
"lazy dogs back "
----------
array[*]=
"the quick brown fox jumped over the lazy dogs back "
A couple of non-obvious key points to make sure your array gets populated with exactly what your command outputs:
If your command output can contain globbing characters than you should disable globbing before the command (oSET="$-"; set -f) and re-enable it afterwards (set +f -$oSET).
If your command output can contain spaces then set IFS to a newline before the command (oIFS="$IFS"; IFS=$'\n') and set it back to it's old value after the command (IFS="$oIFS").
tmp=$(w | awk '{print $1}')
while read i
do
echo "$i"
done <<< "$tmp"
You can use a for loop, i.e.
for user in $(w | awk '{print $1}'); do echo $user; done
which in a script would look nicer as:
for user in $(w | awk '{print $1}')
do
echo $user
done
You can use the xargs command to do this:
w | awk '{print $1}' | xargs -I '{}' id '{}'
With the -I switch, xargs will take each line of its standard input separately, then construct and execute a command line by replacing the specified string '{}' in the command line template with the input line
I guess you should use who instead of w. Try this out,
who | awk '{print $1}' | xargs -n 1 id

Need help in shell script

I am new into shell scripting and learning it for past 2 month. I need your help in tuning or providing any other solution either in sed or AWK for the below question.
"write a script to input the filename and display the content of file in such a manner that each line has only 10 characters.If line in a file exceeds 10 characters then display the rest of the line in next line."
I have written the below script and worked fine. But it took 2 hours for me to write it..(certainly not acceptable. Problem is i know the shell commands very well but still have not mastered the skills to put them into shell scripts :-( . Thanks.
#!/bin/bash
if [ $# -ne 1 ]; then
echo "USAGE: $0 $1"
exit 99;
fi
VAR1=$(echo "$1" | wc -c)
cat "$1" | while read line
do
[ $VAR1 -gt 10 ] && echo "$line" || echo "$line"|tr " " "\n"
done
Using sed
sed 's/........../&\n/g' file.txt
Using grep
grep -oE '.{1,10}' file.txt
Using dd
cat file.txt | dd cbs=10 conv=unblock 2>/dev/null
Using awk?
awk 'BEGIN {FS=""} {for (i=1; i<=NF; i++) if (i % 10 == 0) printf "%s\n", $i ; else if (i == NF) print "\n" ; else printf "%s", $i} ' inputs.txt
This works, but I have a feeling that this is not the most optimal way of using awk :-P

Resources