bash script and greping with command line - bash

new to bash scripting so just wondering if i am doing this code right at all. im trying to search /etc/passwd and then grep and print users.
usage ()
{
echo "usage: ./file.sk user"
}
# test if we have two arguments on the command line
if [ $# != 1 ]
then
usage
exit
fi
if [[ $# < 0 ]];then
usage
exit
fi
# Search for user
fullname=`grep $1 /etc/passwd | cut -f 5 -d :`
firstname=`grep $1 /etc/passwd | cut -f 5 -d : | cut -f 1 -d " "`
#check if there. if name is founf: print msg and line entry
not sure as how to this or if im doing this right...
am i doing this right?

grep $1 /etc/passwd | while IFS=: read -r username passwd uid gid info home shell
do
echo $username: $info
done

This might work for you:
fullname=$(awk -F: '/'$1'/{print $5}' /etc/passwd)
firstname=${fullname/ *}

You're on the right track.
But I think the 2nd if [[ $# < 0 ]] .... fi block doesn't get you much. Your first test case gets the situation right, 'This script requires 1 argument or quits'.
Also, I don't see what you need firstname for, so a basic test is
case "${fullname:--1}" in
-[1] ) printf "No userID found for input=$1\n" ; exit 1 ;;
* )
# assume it is OK
# do what every you want after this case block
;;
esac
You can of course, duplicate this using "${firstname}" if you really need the check.
OR as an equivalent if ... fi is
if [[ "${fullname}" == "" ]] ; then
printf "No userID found for input=$1\n" ; exit 1
fi
note to be more efficient, you can parse ${fullname} to get firstname without all the calls to grep etc, i.e.
firstname=${fullname%% *}
Let me know if you need for me to explain :--1} and %% *} variable modifiers.
I hope this helps.

Instead of this:
fullname=`grep $1 /etc/passwd | cut -f 5 -d :`
firstname=`grep $1 /etc/passwd | cut -f 5 -d : | cut -f 1 -d " "`
Try this:
fullname=$(cut -f5 -d: /etc/passwd | grep "$1")
if [[ $? -ne 0 ]]; then
# not found, do something
fi
firstname=${fullname%% *} # remove the space and everything after
Note that I changed my answer to cut before grep so that it doesn't get false positives if some other field matches the full name you are searching for.

You can simply by reading your input to an array and then printing out your desired fields, something like this -
grep $1 /etc/passwd | while IFS=: read -a arry; do
echo ${arry[0]}:${arry[4]};
done
Test:
jaypal:~/Temp] echo "root:*:0:0:System Administrator:/var/root:/bin/sh" |
while IFS=: read -a arry; do
echo ${arry[0]}:${arry[4]};
done
root:System Administrator

Related

How to cut variables which are beteween quotes from a string

I had problem with cut variables from string in " quotes. I have some scripts to write for my sys classes, I had a problem with a script in which I had to read input from the user in the form of (a="var1", b="var2")
I tried the code below
#!/bin/bash
read input
a=$($input | cut -d '"' -f3)
echo $a
it returns me a error "not found a command" on line 3 I tried to double brackets like
a=$(($input | cut -d '"' -f3)
but it's still wrong.
In a comment the OP gave a working answer (should post it as an answer):
#!/bin/bash
read input
a=$(echo $input | cut -d '"' -f2)
b=$(echo $input | cut -d '"' -f4)
echo sum: $(( a + b))
echo difference: $(( a - b))
This will work for user input that is exactly like a="8", b="5".
Never trust input.
You might want to add the check
if [[ ${input} =~ ^[a-z]+=\"[0-9]+\",\ [a-z]+=\"[0-9]+\"$ ]]; then
echo "Use your code"
else
echo "Incorrect input"
fi
And when you add a check, you might want to execute the input (after replacing the comma with a semicolon).
input='testa="8", testb="5"'
if [[ ${input} =~ ^[a-z]+=\"[0-9]+\",\ [a-z]+=\"[0-9]+\"$ ]];
then
eval $(tr "," ";" <<< ${input})
set | grep -E "^test[ab]="
else
echo no
fi
EDIT:
#PesaThe commented correctly about BASH_REMATCH:
When you use bash and a test on the input you can use
if [[ ${input} =~ ^[a-z]+=\"([0-9]+)\",\ [a-z]+=\"([0-9])+\"$ ]];
then
a="${BASH_REMATCH[1]}"
b="${BASH_REMATCH[2]}"
fi
To extract the digit 1 from a string "var1" you would use a Bash substring replacement most likely:
$ s="var1"
$ echo "${s//[^0-9]/}"
1
Or,
$ a="${s//[^0-9]/}"
$ echo "$a"
1
This works by replacing any non digits in a string with nothing. Which works in your example with a single number field in the string but may not be what you need if you have multiple number fields:
$ s2="1 and a 2 and 3"
$ echo "${s2//[^0-9]/}"
123
In this case, you would use sed or grep awk or a Bash regex to capture the individual number fields and keep them distinct:
$ echo "$s2" | grep -o -E '[[:digit:]]+'
1
2
3

Reading file in while loop bash scripting

I've got this code which reads an example file of /etc/passwd:
#!/bin/bash
OLDIFS=$IFS
IFS=$'\n'
while read linea resto
do
echo $linea
echo $resto
if [[ $(echo $linea | cut -d: -f6 | egrep -c 'al-03-04') == 1 ]]
then
finger $(cut -d: -f1) 2> fich
if [[ $(egrep -c fich) == 1 ]]
then
echo $(echo $linea | cut -d: -f1). Inactive user
else
echo $(echo $linea | cut -d: -f1). Active user
fi
fi
done < <(cat fichpasswd)
IFS=$OLDIFS
and this is the example file of /etc/passwd:
jfer:x:5214:1007:Javier Lopez,,,:/home/al-03-04/jfer:/bin/bash
jperez:x:10912:1009:Juan Perez,,,:/home/al-03-04/jperez:/bin/bash
mfernan:x:10913:1009:Manuel Fernandez,,,:/home/al-02-03/mfernan:/bin/bash
The problem is that the while loop only reads the first line, ignoring the others. The script's output is:
jfer:x:5214:1007:Javier Lopez,,,:/home/al-03-04/jfer:/bin/bash
jfer. Active user
You could try something like :
#!/bin/bash
FILE="test.txt"
while IFS=":" read -a data; do
echo "${data[#]}"
if [[ $(echo ${data[5]}|egrep -c 'al-03-04') -eq 1 ]]; then
if [[ $(finger "${data[0]}" 2>&1) =~ "no such user" ]]; then
echo "${data[0]}. Inactive user"
else
echo "${data[0]}. Active user"
fi
fi
done < "$FILE"
Here's the output :
ineumann ~ $ cat test.txt
ineumann:x:5214:1007:Javier Lopez,,,:/home/al-03-04/jfer:/bin/bash
jperez:x:10912:1009:Juan Perez,,,:/home/al-03-04/jperez:/bin/bash
mfernan:x:10913:1009:Manuel Fernandez,,,:/home/al-02-03/mfernan:/bin/bash
ineumann ~ $ ./test.sh
ineumann x 5214 1007 Javier Lopez,,, /home/al-03-04/jfer /bin/bash
ineumann. Active user
jperez x 10912 1009 Juan Perez,,, /home/al-03-04/jperez /bin/bash
jperez. Inactive user
mfernan x 10913 1009 Manuel Fernandez,,, /home/al-02-03/mfernan /bin/bash
A few comments on your script :
No need to use cat to read your file in a loop.
finger $(cut -d: -f1) 2> fich : cut need an input. And no need to use a temporary file to catch the output of finger (moreover this is not thread safe).
No need to use cut in your script when you choose the right IFS to split a line in multiple parts. In your case, I think the smartest choice would be :.
You can change the IFS only inside the loop with the syntax while IFS=':' read; do ...; done. No need to re-assign IFS with OLDIFS.
You can also use the while IFS=':' read var1 var2 var3 trash; do ...; done syntax to avoid to use an array with read -a (but I'd prefer to use an array as I wrote in my version of your script).

Shell : What does this script do?

#!/bin/bash
if test $# -ne 2
then
echo "Error : Invalid number of arguments"
else
if [ -d $1 ]
then
if [[ $2 =~ ^[0-9]+$ ]]
then
ls -l $1 | while read line
do
eval "echo $line | cut -d' ' -f5" | while read ln
do
if [[ $ln -gt $2 ]]
then
echo $line
fi
done
done
else
echo $2" is not a integer"
fi
else
echo "The repertory "$1" does not exist "
fi
fi
The question was to make cpp , that works like the command cp . The script it's supposed to react correctly if we don't give 2 argument. I don't understand what this script do from line 10 .
This code is the following of this post Explain me 2 lines of this shell script.
Thanks
Without working through the code line by line and explaining it, I would point you at http://explainshell.com, which takes lines of shell code and puts commentary from the manual on each parameter.
E.g, this is part of line 12 above: http://explainshell.com/explain?cmd=echo+%24line+%7C+cut+-d%27+%27+-f5
It should help you go through it line by line and work out what is going on.
In words: It selects the lines from ls -l from a directory $1 which have a size bigger than $2.
If that code is in a file called script.sh, it is called like:
$ ./script.sh /home/user 130000
And it will print all lines of ls -l /home/user which have a size bigger than 130000.
I do not know why the eval in:
eval "echo $line | cut -d' ' -f5" | while read ln
The line will work the same as:
echo $line | cut -d' ' -f5 | while read ln

Why does my bash script hang?

I'm working on a bash script that will check +1000 domains if they are expired. I use a a for loop to iterate over all users in /var/cpanel/users/*. It works great for like the 10 first users (loops) then it just hangs.
A weird thing is that I can stop the script with Ctrl+Z and then start the script again with fg and it continues to work normal for about +10 users but then it hangs again.
This is my scirpt:
# File that will have the result.
file="domain-result.txt"
printf "USER\t\tDOMAIN\t\t\tREPORT\n" > "$file"
printf "\n" >> "$file"
# For loop to iterate over all users in cpanel.
for z in /var/cpanel/users/*;
do
# Only files can be used.
if [[ -f "$z" ]]
then
# Get the domain name.
awk -F'=' '/DNS=/ {print $2}' "$z" | while read row;
do
# If there's no domain name than skip to next account.
if [[ -z "$row" ]]; then continue; fi
printf "Checking domain: %s...done\n" "$row"
# Execute whois command on the domain.
whois=$( /usr/bin/whois $row | grep 'not found' )
# Get the username.
user=$( echo "$z" | awk -F'/' '{print $5}' )
if [[ -n "$whois" ]]
then
printf "%s\t\t%s\t\t%s - EXPIRED\n" "$user" "$row" "$whois" >> "$file"
break
else
continue
fi
done
else
continue
fi
done
printf "\n"
printf "Total: $( sed '1,2d' "$file" | wc -l ) expired domains.\n"
This is a sample of how the files in /var/cpanel/users/* look like:
DNS=stackoverflow.com
Thank you Ignacio Vazquez-Abrams for pointing out WHOIS abuse. I got it to work by adding a sleep 2 to the for loop. Now it works great.

Errors with a shell-script

i found some freaky error. I want to increment a counter, but the variable isnt visible outside the while do.
The script as follows:
## $1 - The file which should be examined
## $2 - The time passed between the checks. If $2 is 5 then all lines from the last 5 minutes are taken
## $3 - The Errormessage to search for
outputOK="OK - nothing happened"
output_logcheck=0;
errlines="";
cat $1 | grep "$3" | while read line
do
linedate=`date -d "$(echo $line | cut -d " " -f 2)" '+%s'`
nowdate=`date '+%s'`
if [ $(( $nowdate - (60 * $2) )) -le $linedate ]
then
$output_logcheck=$[$output_logcheck+1]
$errlines="${errlines} -- ${line}"
fi
done;
if [ $output_logcheck -eq 0 ]
then
echo $outputOK
else
echo "CRITICAL - There are -= ${output_logcheck} =- $3 -- Lines: $errlines"
fi
So i dont know what else to try.
Thanks in advance.
The problem is that pipe create a SubShell.
change
cat $1 | grep "$3" | while read line
do
...
done
to
while read line
do
...
done <(cat $1 | grep "$3")
As noted, the Bash shell, creates a subshell whenever a pipe is opened to a loop. In that case, variables within the loop are local to the loop.
One kludge is to substitute (if possible) a Korn ('ksh') shell for the Bash one.
Try something like:
## $1 - The file which should be examined
## $2 - The time passed between the checks. If $2 is 5 then all lines from the last 5 minutes are taken
## $3 - The Errormessage to search for
outputOK="OK - nothing happened"
outfile="/tmp/result.$$"
trap { rm $outfile } 0 1 2 3
cat $1 | grep "$3" | (output_logcheck=0; errlines=""; while read line
do
linedate=`date -d "$(echo $line | cut -d " " -f 2)" '+%s'`
nowdate=`date '+%s'`
if [ $(( $nowdate - (60 * $2) )) -le $linedate ]
then
$output_logcheck=$[$output_logcheck+1]
$errlines="${errlines} -- ${line}"
fi
done; echo $output_logcheck ":" $errlines > $outfile)
output_logcheck=`cat $outfile| cut -f1 -d:`
errlines=`cat $outfile|cut -f2 -d:`
if [ $output_logcheck -eq 0 ]
then
echo $outputOK
else
echo "CRITICAL - There are -= ${output_logcheck} =- $3 -- Lines: $errlines"
fi
while is executed in a separate process. Variables that are changed in the context of that process still hold their unchanged valus in the parent process.

Resources