Read all lines except commented ones, lines containing 'bind|swap|shm' from/etc/fstab and print the ones in them that are not mounted? - bash

Read all lines except commented ones, lines containing 'bind|swap|shm' from/etc/fstab and print the ones in them that are not mounted? I have tried the below and I was wondering if I could get a better shorter code.
mount_check()
{
fstb=$(cat /etc/fstab |egrep -vw 'bind|swap' |awk '$1 !~/#|^$/ {print $2}')
for i in ${fstb}
do
df -hPT | grep -wq ${i}
if [ $? -eq 1 ]
then
echo "The file system ${i} has an entry in /etc/fstab file but not mounted"
fi
done
}rc_mount_check=`mount_check |tee |wc -l`if [ $rc_mount_check -eq '0' ]
then
echo -e "OK: All file systems listed in /etc/fstab are mounted"
exit $OK
else
echo -e "CRITICAL: Please verify and mount the file systems\n$(mount_check)\n"
exit $CRITICAL
fi

Actual command which you are looking for is findmnt. Could you please try following.
awk '
!/bind|swap|shm/ && $1 !~/#|^$/{
system("if [[ -n $(findmnt -m " $2 ") ]]; \
then echo Mount " $2 " is mounted.;\
else echo Mount " $2 " is NOT mounted.;\
fi"\
)
}
' /etc/fstab
OR in form of one-liner try following(in case you need it):
awk '!/bind|swap|shm/ && $1 !~/#|^$/ {system("if [[ -n $(findmnt -m " $2 ") ]]; then echo Mount " $2 " is mounted.;else echo Mount " $2 " is NOT mounted.;fi")}' /etc/fstab

Assuming the currently accepted answer:
awk '
!/bind|swap|shm/ && $1 !~/#|^$/{
system("if [[ -n $(findmnt -m " $2 ") ]]; \
then echo Mount " $2 " is mounted.;\
else echo Mount " $2 " is NOT mounted.;\
fi"\
)
}
' /etc/fstab
does what you want, you should actually do something like this (untested) instead:
mounts=( $(awk '!/bind|swap|shm/ && $1 !~/#|^$/ { print $2 }' /etc/fstab) )
for mount in "${mounts[#]}"; do
if [[ -n $(findmnt -m "$mount") ]]
then echo "Mount $mount is mounted."
else echo "Mount $mount is NOT mounted."
fi
done
i.e. don't call shell to call awk to call system to call shell to call findmnt. Just have shell 1) call awk to get whatever list of mounts that first awk line spits out and then 2) call findmnt on that list. That way you're not spawning subshells upon subshells and 5 layers deep for every mount.

Related

How to take the first field of each line from a text file and execute it in a while-loop?

I tried with below script. But, not working for cut the first field of each line and to be executed for "chmod".
#!/bin/bash
if [ -z "$1" ]; then
echo -e "Usage: $(basename $0) FILE\n"
exit 1
fi
if [ ! -e "$1" ]; then
echo -e "$1: File doesn't exist.\n"
exit 1
fi
while read -r line; do
awk '{print $1}'
[ -n "$line" ] && chown root "$line" && echo -e "$line Ownership changed"
done < "$1"
If field separator is space, try this:
while read -r line; do
FILE_TO_CHANGE=$(echo $line | awk '{print $1}')
[ -n "$line" ] && chown root "$FILE_TO_CHANGE" && echo -e "$line Ownership changed"
done < "$1"
awk read $line and print first token on standard output, the result is saved in FILE_TO_CHANGE variable and then it is used to run chown.
Another way could be:
awk '{print $1}' $1 | while read line; do
chown root "$line" && echo -e "$line Ownership changed"
done
awk read your file and print the first field of each line, in this case, while loop read awk output line by line and run chown on field.
You could extract the first word on each line with awk and pipe to xargs, invoking chown only as few times as possible:
awk '{print $1}' "$1" | xargs chown root

bash script loop to check if variable contains string - not working

i have a script which copy files from one s3 bucket to local server, do some stuff and upload it to another s3 bucket.
in the original bucket i have few folders, one of them called "OTHER"
i dot want my script to work on this folder
i tried to define a loop to check if the path string does not contains the string "OTHER" only then to continue to other commands but for some reason it is not working.
what am i doing wrong ?
#!/bin/bash
shopt -s extglob
gcs3='s3://gc-reporting-pud-production/splunk_printer_log_files/'
gcs3ls=$((aws s3 ls 's3://gc-reporting-pud-production/splunk_printer_log_files/' --recursive) | sed 's/^.*\(splunk_printer.*\)/\1/g'| tr -s ' ' | tr ' ' '_')
ssyss3=s3://ssyssplunk
tokenFile=/splunkData/GCLogs/tokenFile.txt
nextToken=$((aws s3api list-objects-v2 --bucket "gc-reporting-pud-production" --prefix splunk_printer_log_files/ --max-items 5) |grep -o 'NEXTTOKEN.*' |awk -F " " '{print $2}')
newToken=$( tail -n 1 /splunkData/GCLogs/tokenFile.txt )
waterMark=$(aws s3api list-objects-v2 --bucket "gc-reporting-pud-production" --prefix splunk_printer_log_files/ --max-items 5 --starting-token
$newToken|sed 's/^.*\(splunk_printer.*zip\).*$/\1/'|sed '1d'|sed '$d')
while true; do
for j in $waterMark ; do
echo $j
if [ "$j" != *"OTHER"* ]; then
gcRegion=$(echo $j | awk -F'/' '{print $2}')
echo "gcRegion:"$gcRegion
if [ "$gcRegion" != "OTHER" ]; then
gcTech=$(echo $j | awk -F'/' '{print $3}')
echo "GCTech:"$gcTech
gcPrinterFamily=$(echo $j | awk -F'/' '{print $4}')
echo "gcPrinterFamily:" $gcPrinterFamily
gcPrinterType=$(echo $j | awk -F'/' '{print $5}')
echo "gcPrinterType:" $gcPrinterType
gcPrinterName=$(echo $j| awk -F'/' '{print $6}')
echo "gcPrinterName:" $gcPrinterName
gcFileName=$(echo $j| awk -F'/' '{print $7}'| awk -F'.zip' '{print $1}')
echo "gcFileName:" $gcFileName
cd /splunkData/GCLogs
dir="/splunkData/GCLogs/$gcRegion/$gcTech/$gcPrinterFamily/$gcPrinterType/$gcPrinterName"
echo "dir:"$dir
mkdir -p $dir
aws s3 sync $gcs3$gcRegion/$gcTech/$gcPrinterFamily/$gcPrinterType/$gcPrinterName/ $dir
find $dir -name '*.zip' -exec sh -c 'unzip -o -d "${0%.*}" "$0"' '{}' ';'
aws s3 cp $dir $ssyss3/$gcRegion/$gcTech/$gcPrinterFamily/$gcPrinterType/$gcPrinterName/ --recursive --exclude "*.zip"
newToken=$( tail -n 1 /splunkData/GCLogs/tokenFile.txt )
nextToken=$(aws s3api list-objects-v2 --bucket "gc-reporting-pud-production" --prefix splunk_printer_log_files/ --max-items 5 --starting-token $newToken |grep -o 'NEXTTOKEN.*' |awk -F " " '{print $2}')
waterMark=$(aws s3api list-objects-v2 --bucket "gc-reporting-pud-production" --prefix splunk_printer_log_files/ --max-items 5 --starting-token $newToken|sed 's/^.*\(splunk_printer.*zip\).*$/\1/'|sed '1d'|sed '$d')
echo "$nextToken" > "$tokenFile"
fi
fi
done
done
You need to use the double-bracket conditional command to turn == and != into pattern matching operators:
if [[ "$j" != *"OTHER"* ]]; then
# ^^ ^^
Or use case
case "$j" in
*OTHER*) ... ;;
*) echo "this is like an `else` block" ;;
esac
Paste your code into https://www.shellcheck.net/ for other things to fix.
I think glenn jackman was on the right path. Try this:
if [[ "$j" != *OTHER* ]]; then
The [[ ]] is required for pattern string matching (and you have to remove the " ). The case statement is also a good idea. You can abandon the shell test altogether and use grep as follows:
if
grep -q '.*OTHER.*' <<< "$j" 2>/dev/null
then
...
fi
Here's a check of the [[ ]]:
$ echo $j
abOTHERc
$ [[ "$j" == *OTHER* ]]
$ echo $?
0
As per BenjaminW., the quotes around $j in [[ ]] are unnecessary. However, the quotes around *OTHER* do make a big difference. See below:
$ j="OTHER THINGS"
$ [[ $j == "*OTHER*" ]] ; echo "$j" matches '"*OTHER*"': $?
OTHER THINGS matches "*OTHER*": 1
$ [[ $j == *OTHER* ]] ; echo "$j" matches '*OTHER*': $?
OTHER THINGS matches *OTHER*: 0

I need to compare two variables/interfaces and it is not working

IPv4=$( ifconfig |grep -v 'eth1:' |grep -A 1 'eth1'| tail -1 |cut -d ':' -f 2 |cut -d ' ' -f 1)
IPnode1=$"111.22.333.44"
IPnode2=$"111.22.333.45"
ifconfig |grep -v 'eth1:' |grep -A 1 'eth1'| tail -1 |cut -d ':' -f 2 |cut -d ' ' -f 1
if [[ "$IPv4" = "$IPnode1" ]]; then
echo "found the address "
echo "111.22.333.44 VM01.com VM01" >> /etc/hosts
else
echo "The address does not match"
fi
ifconfig |grep -v 'eth1:' |grep -A 1 'eth1'| tail -1 |cut -d ':' -f 2 |cut -d ' ' -f 1
if [[ "$IPv4" = "$IPnode2" ]]; then
echo ""
echo "found the address "
echo "111.22.333.45 VM02.com VM02" >> /etc/hosts
else
echo "The address does not match"
fi
Your code will always report "The address does not match". If the address matches $IPnode1, then it doesn't match $IPnode2, and vice versa. You should only execute the second test when the first one fails.
if [[ "$IPv4" = "$IPnode1" ]]; then
echo "found the address "
echo "$IPnode1 VM01.com VM01" >> /etc/hosts
elif [[ "$IPv4" = "$IPnode2" ]]; then
echo "found the address "
echo "$IPnode2 VM02.com VM02" >> /etc/hosts
else
echo "The address does not match"
fi

bash script grep the etc file for user information

I am trying to write a script in bash to read in arguments (usernames) and then return information about that username by searching the /etc/passwd file. I am not the root on the server that I am scripting this on, but I don't need to be. Here is the code that I have so far, but I dont think I am using the grep command right.
#!/bin/bash
if [ $# -eq 1 ]; then #if num of args =1; then continue on
UNAME=$1 #set arg to var UNAME
grep UNAME=$(grep $/etc/passwd)
if [ $UNAME == /etc/passwd ]; then #if UNAME exists then display info below
echo "-------------------------------------------"
echo "Username: $(grep UNAME/etc/passwd/ $f1) "
echo "User ID (UID): $(grep UNAME/etc/passwd/) "
echo "Group ID (GID): $(grep UNAME/etc/passwd/) "
echo "User info: $(grep UNAME/etc/passwd/) "
echo "Home directory: $(grep UNAME/etc/passwd/) "
echo "Command shell: $(grep UNAME/etc/passwd/) "
echo "-------------------------------------------"
else #if UNAME is nonexistant then display this error message
echo "-------------------------------------------"
echo ""$UNAME" does not exist."
echo "-------------------------------------------"
fi
fi
if [ $# -eq 0 ]; then #if num of args =0; then display this
echo "Not enough arguments provided."
echo "USAGE: $0 <user_name> [user_name] ..."
exit 1
fi
You're doing too much. grep is the wrong tool for the type of parsing you are trying to do. The solution given here is pretty sloppy since it reads the passwd file once for each user you are querying instead of reporting everything in one pass, but it's not unreasonable:
#!/bin/sh
for UNAME; do
while IFS=: read login passwd uid gid name home shell; do
if test "$login" = "$UNAME"; then
echo "-------------------------------------------"
echo "Username: $login"
echo "User ID (UID): $uid"
echo "Group ID (GID): $gid"
echo "User info: $name"
echo "Home directory: $home"
echo "Command shell: $shell"
echo "-------------------------------------------"
continue 2
fi
done < /etc/passwd
echo "No entry for user $UNAME" >&2
done
Before I get to the explanation, here is how I would have written your script:
cat ./test.sh
#!/bin/bash
UNAME=${1} #set arg to var UNAME
if [ -z ${UNAME} ]; then #if no argument is provided; then display this
echo "Not enough arguments provided."
echo "USAGE: $0 <user_name>"
exit 1
fi
if grep ${UNAME} /etc/passwd >/dev/null; then #if UNAME exists then display info below
echo "-------------------------------------------"
echo "Username: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $1}' /etc/passwd)"
echo "User ID (UID): $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $3}' /etc/passwd)"
echo "Group ID (GID): $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $4}' /etc/passwd)"
echo "User info: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $5}' /etc/passwd)"
echo "Home directory: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $6}' /etc/passwd)"
echo "Command shell: $(awk -F ':' -v uname=${UNAME} '$0 ~ uname {print $7}' /etc/passwd)"
echo "-------------------------------------------"
else #if UNAME is nonexistant then display this error message
echo "-------------------------------------------"
echo "\"${UNAME}\" does not exist."
echo "-------------------------------------------"
fi
Explanation:
if can use grep directly to check for the existence of
your argument in passwd
UNAME= is how arguments are assigned, not
called - they are called with the $ - for example $UNAME
/etc/passwd is an absolute file name and not an argument, thus
$/etc/passwd doesn't mean anything to bash
argument=$(any old bash command) is telling bash that you want to
assign the output of "any old bash command" to "argument", so $()
has to contain a command with output for "argument" to be assigned
anything.
the >/dev/null sends the output of grep to the waste basket because we only care at this point whether or not it's successful so that if knows what to do
grep works like this: grep <the thing you want to find> <file> (note that spaces matter and to use "" if you want to include spaces in your search)
to get specific fields, awk is more useful. awk -F':' tells awk that I want : to define my field separators; -v uname=${UNAME} passes my argument to awk; within awk, $0 ~ uname checks the line for my argument (like grep); and {print $1} prints the first field, and so on
Also consider the limitation of partial matches. If, for example, I have a user JSmith and Smith, searching Smith will return both. This can be addressed with regex but will add a great deal of complexity to the script.
Here is a version with lighter awk usage which works with multiple arguments:
#!/bin/bash
for username; do
if grep ${username} /etc/passwd >/dev/null; then #if username exists then display info below
echo "---------------------------------------"
awk -F':' -v uname="${username}" '\
$0 ~ uname \
{print " Username: "$1\
"\n User ID (UID): "$3\
"\n Group ID (GID): "$4\
"\n User info: "$5\
"\n Home directory: "$6\
"\n Command shell: "$7}' /etc/passwd
echo "---------------------------------------"
else #if username is nonexistant then display this error message
echo "---------------------------------------"
echo "\"${username}\" does not exist."
echo "---------------------------------------"
fi
done
if [ -z ${1} ]; then #if no argument is provided; then display this
echo " Not enough arguments provided."
echo " USAGE: $0 <user name>"
exit 1
fi
*Credit to William Pursell for explaining the for loop
The best tool to read databases such as passwd is getent. This will fetch entries from a database supported by Name Service Switch Libraries. If you really need to limit reading the files databases, you can tell getent to use files database with -s files.
Your code is unnecessarily long. Fetching a record and parsing them into multiple variables should be enough.
#!/bin/bash
if [ $# -eq 1 ]; then #if num of args =1; then continue on
if IFS=: read username ignore uid gid gecos home shell < <(getent -s files passwd $1); then
echo "-------------------------------------------"
echo "Username: $username"
echo "User ID (UID): $uid"
echo "Group ID (GID): $gid"
echo "User info: $gecos"
echo "Home directory: $home"
echo "Command shell: $shell"
echo "-------------------------------------------"
else #if $1 is nonexistant then display this error message
echo "-------------------------------------------"
echo "'$1' does not exist."
echo "-------------------------------------------"
fi
fi
if [ $# -eq 0 ]; then #if num of args =0; then display this
echo "Not enough arguments provided."
echo "USAGE: $0 <user_name> [user_name] ..."
exit 1
fi

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.

Resources