issues with grep variable and white space - linux

I am trying to get this bit of code to work. and I am getting hung up on the second piped grep with the variable $pkgname. I am unable to find a way to get it to read the variable correctly either I get no output or as the code is currently written I get grep: illegal byte sequence. if I put either text with no space in the variable it works or I enter the text as part of the grep statement it works.
#!/bin/bash
counter=0
results2=Executing\ SSH\ MOTD\ banner
pkgname=SSH\ MOTD\ banner
until [ $counter = 1 ]
do
echo $counter
echo $pkgname
echo $results2
result=$(grep "$(date +"%b %d")" /var/log/test.log | grep “$pkgname” | cut -d':' -f 4 | sed 's/^ *//g')
echo $result
if [ “$result” == “$results2” ]; then
counter=1
fi
done
echo finished
so the log file line I am looking for looks like this.
Tue Jun 28 10:58:57 machinename process: Executing SSH MOTD banner

change to
pkgname="SSH MOTD banner" # Here use quotes to avoid using \
As [ #jack ] rightly pointed out in this [ comment ], you need the neutral quotation mark " for a variable to be expanded. That said you can simplify the regex to below
pkgname="SSH MOTD banner"
d=$(date +"%b %d")
result="$(awk -v FS=":" -v d="$d" -v pkg="$pkg_name" '{if($0 ~ date && $0 ~ pkg){sub(/^ */,"",$4);print $4}}' /var/log/test.log)"

Related

Command substitution with strings in Bash [duplicate]

This question already has answers here:
Why does adding spaces around bash comparison operator change the result?
(2 answers)
How to pass the value of a variable to the standard input of a command?
(9 answers)
Closed 1 year ago.
I want to check which lines of the file /etc/passwd end with the "/bin/bash" string (field number 7, ":" as delimiter).
So far, I've written the following code:
while read line
do
if [ $("$line" | cut -d : -f 7)=="/bin/bash" ]
then
echo $line | cut -d : -f 1
echo "\n"
fi
done < /etc/passwd
Currently, executing the script throws errors that show a bad interpretation (most likely due to the syntax).
I'd appreciate if you could help me.
You MUST surround the == operator with spaces. [ and [[ do different things based on how many arguments are given:
if [ "$( echo "$line" | cut -d: -f7 )" == "/bin/bash" ]; ...
I would actually do this: parse the line into fields while you're reading it.
while IFS=: read -ra fields; do
[[ ${fields[-1]} == "/bin/bash" ]] && printf "%s\n\n" "${fields[0]}"
done < /etc/passwd
This line is wrong:
if [ $("$line" | cut -d : -f 7)=="/bin/bash" ]
Also, this is not going to do what you want:
echo "\n"
Bash echo doesn't understand backslash-escaped characters without
-e. If you want to print a new line use just echo but notice that
the previous echo:
echo $line | cut -d : -f 1
will add a newline already.
You should always check your scripts with
shellcheck. The correct script would be:
#!/usr/bin/env bash
while read -r line
do
if [ "$(echo "$line" | cut -d : -f 7)" == "/bin/bash" ]
then
echo "$line" | cut -d : -f 1
fi
done < /etc/passwd
But notice that you don't really need a loop which is very slow and
could use the following awk one-liner:
awk -v FS=: '$7 == "/bin/bash" {print $1}' /etc/passwd
Instead of looping through the rows, and then checking for the /bin/bash part, why not use something like grep to get all the desired rows, like so:
grep ':/bin/bash$' /etc/passwd
Optionality, you can loop over the rows by using a simple while;
grep ':/bin/bash$' /etc/passwd | while read -r line ; do
echo "Processing $line"
done
Don't do while read | cut. Use IFS as:
#!/bin/sh
while IFS=: read name passwd uid gid gecos home shell; do
if test "$shell" = /bin/bash; then
echo "$name"
fi
done < /etc/passwd
But for this particular use case, it's probably better to do:
awk '$7 == "/bin/bash"{print $1}' FS=: /etc/passwd
The issue your code has is a common error. Consider the line:
if [ $("$line" | cut -d : -f 7)=="/bin/bash" ]
Assume you have a value in $line in which the final field is /bin/dash. The process substitution will insert the string /bin/dash, and bash will attempt to execute:
if [ /bin/dash==/bin/bash ]
since /bin/bash==/bin/bash is a non-empty string, the command [ /bin/bash==/bin/bash ] returns succesfully. It does not perform any sort of string comparison. In order for [ to do a string comparison, you need to pass it 4 arguments. For example, [ /bin/dash = /bin/bash ] would fail. Note the 4 arguments to that call are /bin/dash, =, /bin/bash, and ]. [ is an incredibly bizarre command that requires its final argument to be ]. I strongly recommend never using it, and replacing it instead with its cousin test (very closely related, indeed both test and [ used to be linked to the same executable) which behaves exactly the same but does not require its final argument to be ].

extract only url segment not the below regular expression in shell script?

My logic goes here,
vodCIntSvc_VideoSegments()
{
# logic to download video segments from mediaPlaylist-Video payload response based on the count
var_param1="#EXTINF:6.006,"
var_segment_cnt=$(echo $var_videoLayer | awk -F"$var_param1" '{print NF}')
echo $var_segment_cnt " count is "
if [ $input_segment_cnt -le $var_segment_cnt ]; then
var_segment_cnt=$(($input_segment_cnt+1))
fi
i=2
while [ $i -le $var_segment_cnt ]
do
segment_url=$(echo $var_videoLayer | awk -F"$var_param1" '{print $'$i'}')
#"#EXTINF:6.006,
echo $segment_url
i=$((i+1))
echo $segment_url >> /tmp/"$directory_name"/4_videoSegments_$(date +%Y%m%d%H%M).ts
done
echo "$(date +'%Y-%m-%d %H:%M:%S') :: Downloading video segments are successful with argument count- $input_segment_cnt & the output file is saved to :- /tmp/"$directory_name"/4_VideoSegments_$(date +%Y%m%d%H%M).ts"
}
and the output which I Am getting is :
https://manifest.vod.wb4.ott.eng.alticeusa.net/cdn/wb4vodmgmt7idpx01b1.wb4-7.eng.cv.net:5555/hls/NCPHAAAAAOBLNAOF.m3u8/Level(6)/Segment(0).ts **#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://indemand.com~INMV1019201000659085",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1",IV=0x0C672EE95BE69AD3AEB30B44A04E966A**
https://manifest.vod.wb4.ott.eng.alticeusa.net/cdn/wb4vodmgmt7idpx01b1.wb4-7.eng.cv.net:5555/hls/NCPHAAAAAOBLNAOF.m3u8/Level(6)/Segment(1).ts **#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://indemand.com~INMV1019201000659085",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1",IV=0x0C672EE95BE69AD3AEB30B44A04E966A**
the regular expressions #EXT-X-KEY for each url should be removed
and the expected output is :
https://manifest.vod.wb4.ott.eng.alticeusa.net/cdn/wb4vodmgmt7idpx01b1.wb4-7.eng.cv.net:5555/hls/NCPHAAAAAOBLNAOF.m3u8/Level(6)/Segment(0).ts
https://manifest.vod.wb4.ott.eng.alticeusa.net/cdn/wb4vodmgmt7idpx01b1.wb4-7.eng.cv.net:5555/hls/NCPHAAAAAOBLNAOF.m3u8/Level(6)/Segment(1).ts
could someone please provide any suggestions in shell script on above my code.
One way using sed:
echo "$segment_url" | sed 's/#EXT-X-KEY.*//'
This will remove text starting from #EXT-X-KEY till the end of the line.
Try following in awk:
echo "$segment_url" | awk '{gsub(/#EXT-X-KEY.*/,"")}1'
I used gsub here for global substitution you can remove it to sub in case you don't want global substitution here.
Correction 1: gsub(/#EXT-X-KEY.*/,"") added / after *.

Add users in shell script with txt file

I want to add some users who are in this file like:
a b
c d
e f
firstname lastname always
#!/bin/bash
Lines=$(cat newusers.txt | wc -l)
first=$(cat newusers.txt | awk '{print $1}')
last=$(cat newusers.txt | awk '{print $2}')
#test
echo $Lines;
echo $first;
echo $last;
until [ -z $1]; then
useradd - m -d /home/$1 -c "$1 + $2" $1
fi
before loop it works fine but I can't add newline.
The echo shows a c e and second for lastname b d f.
I tried to add newline in but it doesn't works.
What can i use for this?
Because I guess I can't add the user because of the newline problem.
I also searched on stackoverflow to find out a way to check if the user already exists by /dev/null but which variable do i have to use for it?
It's easier to process the file line by line:
while read first last ; do
useradd -m -d /home/"$first" -c "$fist + $last" "$first"
done < newusers.txt
I do not understand what you mean to do by your code, but if you want to read the file line by line and get the values of different fields then you can use the following code snippet:
#!/bin/bash
filename="newusers.txt"
while read -r line
do
fn=$( echo "$line" |cut -d" " -f1 )
ln=$( echo "$line" |cut -d" " -f2 )
echo "$fn $ln"
done < "$filename"
Note: You cannot add users the way you want to using bash script; since you will be prompted for password which must be supplied using tty you can use expect to program it; or use system calls.

Retrieve string between characters and assign on new variable using awk in bash

I'm new to bash scripting, I'm learning how commands work, I stumble in this problem,
I have a file /home/fedora/file.txt
Inside of the file is like this:
[apple] This is a fruit.
[ball] This is a sport's equipment.
[cat] This is an animal.
What I wanted is to retrieve words between "[" and "]".
What I tried so far is :
while IFS='' read -r line || [[ -n "$line" ]];
do
echo $line | awk -F"[" '{print$2}' | awk -F"]" '{print$1}'
done < /home/fedora/file.txt
I can print the words between "[" and "]".
Then I wanted to put the echoed word into a variable but i don't know how to.
Any help I will appreciate.
Try this:
variable="$(echo $line | awk -F"[" '{print$2}' | awk -F"]" '{print$1}')"
or
variable="$(awk -F'[\[\]]' '{print $2}' <<< "$line")"
or complete
while IFS='[]' read -r foo fruit rest; do echo $fruit; done < file
or with an array:
while IFS='[]' read -ra var; do echo "${var[1]}"; done < file
In addition to using awk, you can use the native parameter expansion/substring extraction provided by bash. Below # indicates a trim from the left, while % is used to trim from the right. (note: a single # or % indicates removal up to the first occurrence, while ## or %% indicates removal of all occurrences):
#!/bin/bash
[ -r "$1" ] || { ## validate input is readable
printf "error: insufficient input. usage: %s filename\n" "${0##*/}"
exit 1
}
## read each line and separate label and value
while read -r line || [ -n "$line" ]; do
label=${line#[} # trim initial [ from left
label=${label%%]*} # trim through ] from right
value=${line##*] } # trim from left through '[ '
printf " %-8s -> '%s'\n" "$label" "$value"
done <"$1"
exit 0
Input
$ cat dat/labels.txt
[apple] This is a fruit.
[ball] This is a sport's equipment.
[cat] This is an animal.
Output
$ bash readlabel.sh dat/labels.txt
apple -> 'This is a fruit.'
ball -> 'This is a sport's equipment.'
cat -> 'This is an animal.'

Getting a specific line from a string where the line number I must get is stored in a variable?

I'm trying to get a specific line of a variable. The line I must get is stored in i. My code looks like this right now.
$(echo "$data" | sed '$iq;d')
It looks like I'm putting i in there wrong, Putting a number in for i works fine but $i gets me the entire string.
I haven't found a solution that works with a variable yet and I'm not too familiar with bash and would appreciate help,
Edit: a bit of context
i=5
data=$(netstat -a | grep ESTAB)
line=$(echo "$data" | sed "${i}p")
echo $line
Use sed -n "${i}p" instead.
Example:
i=4; seq 1 10 | sed -n "${i}p"
Output:
4
Bonus:
i=5
readarray -O 1 -t data < <(exec netstat -a | grep ESTAB) ## Stores data as an array of lines starting at index 1
line=${data[i]}
echo "$line"
# printf '%s\n' "${data[#]}" ## Prints whole data.
Here is way you can do this in BASH itself:
IFS=$'\n' arr=($data)
echo "${arr[$i]}"

Resources