LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-17-2020, 02:28 PM   #1
uncorrupt3d
LQ Newbie
 
Registered: Mar 2020
Location: Pluto (it's a planet)
Distribution: LFS
Posts: 9

Rep: Reputation: Disabled
Same bash script has different behavior in 2 separate scripts. Thoughts?


Hey guys,

So I've been working on a really large project for work and I can't seem to crack this bug right now... So I'm trying to get my script to verify all users' home directories exist, using the following code:

Code:
#!/bin/bash
grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '($7 != "'"$which nologin)"'" && $7 != "/bin/false") { print $1 " " $6 }' | while read -r user dir; 
do if [ ! -d "$dir" ]; 
then echo "The home directory ($dir) of user $user does not exist.";
fi;
done;
When I run this as an individual shell script, I get several outputs that look somewhat like:
Code:
The home directory (HOME_DIR_PATH) of user USERNAME does not exist.
However, when I remove the #!/bin/bash line and copy the rest of the text into a function inside my shell script (same exact thing), I get the following behavior on an infinite loop:
Code:
The home directory () of user does not exist.
Do you guys have any ideas? I've tried running a separate script with this chunk of bash code from my project to resolve this, rewriting the logic entirely and using shell code checkers- I originally had a syntax issue with awk.

Your help matters a ton! Thx
 
Old 03-17-2020, 04:26 PM   #2
MensaWater
LQ Guru
 
Registered: May 2005
Location: Atlanta Georgia USA
Distribution: Redhat (RHEL), CentOS, Fedora, CoreOS, Debian, FreeBSD, HP-UX, Solaris, SCO
Posts: 7,831
Blog Entries: 15

Rep: Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669Reputation: 1669
It is because you're piping into your while loop at the beginning rather than redirecting at the end. With the pipe the variables are local and can't be used elsewhere such as within a function.

Code:
while read -r user dir
do if [ ! -d "$dir" ] 
then echo "The home directory ($dir) of user $user does not exist."
fi
done < <grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '($7 != "'"$which nologin)"'" && $7 != "/bin/false") { print $1 " " $6 }'
Note there is a space between the two less than signs. The first less than is a redirect into the done.
The second less than is "process substitution" based on the grep - you may need to encapsulate the grep and what follows in parentheses for it work.
 
1 members found this post helpful.
Old 03-17-2020, 06:01 PM   #3
uncorrupt3d
LQ Newbie
 
Registered: Mar 2020
Location: Pluto (it's a planet)
Distribution: LFS
Posts: 9

Original Poster
Rep: Reputation: Disabled
Thanks! This seems to work, I'll bump the thread if this part of the function throws any more errors.
 
Old 03-18-2020, 02:31 AM   #4
MadeInGermany
Senior Member
 
Registered: Dec 2011
Location: Simplicity
Posts: 2,808

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
I would prefer pure shell builtins
Code:
# The following line is clumsy
wnologin=$(which nologin)

while IFS=":" read user pw uid gid gecos dir shell
do
  case $user in (halt|sync|shutdown) continue;; esac
  case $shell in ($wnologin|/bin/false) continue;; esac
  if [ ! -d "$dir" ]
  then
    echo "The home directory ($dir) of user $user does not exist."
  fi
done < /etc/passwd

Last edited by MadeInGermany; 03-18-2020 at 12:57 PM. Reason: Fixed a typo
 
1 members found this post helpful.
Old 03-18-2020, 07:28 AM   #5
uncorrupt3d
LQ Newbie
 
Registered: Mar 2020
Location: Pluto (it's a planet)
Distribution: LFS
Posts: 9

Original Poster
Rep: Reputation: Disabled
EDIT: still doesn't work

I tried both of the methods, and apparently it's still not working in my test lab vm. I'm beginning to wonder if it's an environment thing with CentOS 8... Because it worked at home.
 
Old 03-18-2020, 08:00 AM   #6
boughtonp
Senior Member
 
Registered: Feb 2007
Location: UK
Distribution: Debian
Posts: 3,616

Rep: Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555Reputation: 2555

The version MadeInGermany posted is definitely clearer.

It also makes it easy to add debug statements in, to test at which point things are differing from expectations...

 
Old 03-18-2020, 08:26 AM   #7
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,966

Rep: Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332
when you use pipe in shell scripts you will automatically force bash to use child processes.
So all your awk/grep/... will run in a new shell, but also your while loop will be put in a subshell.

Additionally post #2 looks incorrect without parenthesis:
Code:
while read -r user dir
do if [ ! -d "$dir" ] 
then echo "The home directory ($dir) of user $user does not exist."
fi
done < <(grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '($7 != "'"$which nologin)"'" && $7 != "/bin/false") { print $1 " " $6 }')
looks much better - and probably works
(this is just a comment: this grep and awk can be combined into one single awk)
 
Old 03-18-2020, 08:53 AM   #8
shruggy
Senior Member
 
Registered: Mar 2020
Posts: 3,677

Rep: Reputation: Disabled
If you just copied the MadeInGermany's version without checking the syntax, be aware that there's a typo in the second case statement:

contine → continue
 
1 members found this post helpful.
Old 03-18-2020, 09:06 AM   #9
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,009

Rep: Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193Reputation: 3193
I have to say that on any relatively new system, I feel as though there are plenty of issues to be had with some of the items in this script.
Specifically:

1. Many systems these days have /bin, /sbin and /usr/sbin as symbolic links to /usr/bin. Now depending on your PATH value, which will return the first place it finds any command.
So using 'which nologin' is erroneous at best, here's an example on my home machine:
Code:
$ echo $PATH
/home/grail/.rbenv/shims:/usr/sbin:/sbin:/bin:/usr/games:/home/grail/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/grail/bin:/home/grail/go/bin
# I have highlighted the first (s)bin entry in my PATH
$ which nologin
/usr/sbin/nologin
$ awk -F: '$7 ~ /nologin/{print $7}' /etc/passwd
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/nologin
/usr/bin/nologin
/sbin/nologin
/usr/bin/nologin
/usr/bin/nologin
/sbin/nologin
/usr/bin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
As you can see, I have a large number of users using nologin from a variety of places and one would think all should be excluded??

2. /bin/false -- see above as users could have /sbin/false, /usr/bin/false and /usr/sbin/false ... again, should you be limiting yourself?

3. grep -E -v '^(halt|sync|shutdown)' -- what if you have a user that starts with one of these words but has additional characters? (eg. sync-backup)

4. Try not to string multiple commands together when you are doubling up on abilities

As you have a solution in all bash, here's another alternative in awk:
Code:
awk -F: '$1 !~ /^(halt|sync|shutdown)$/ && $7 !~ /(nologin|false)/ && system("[[ -d "$6" ]]"){print "tada -- "$0}' /etc/passwd
 
1 members found this post helpful.
Old 03-18-2020, 09:13 AM   #10
michaelk
Moderator
 
Registered: Aug 2002
Posts: 25,759

Rep: Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931Reputation: 5931
What do you mean by not work? What is your test lab VM?

I don't have CentOS 8 installed yet but the only difference I noticed with MadeInGermany's script as compared to CentOS 7 is which nologin returns /usr/sbin/nologin but /etc/passwd uses /sbin/nologin for a regular user.

By default which (without any options) returns the first match and with CentOS7 /usr/sbin is in the path before /sbin for a regular user. So yes it could be an environment thing.

I would not use a variable named dir since it is an actual command but that does not seem to break the script.

A bit to late grail beat me to the punch...

Last edited by michaelk; 03-18-2020 at 09:15 AM.
 
Old 03-18-2020, 11:59 AM   #11
Kenhelm
Member
 
Registered: Mar 2008
Location: N. W. England
Distribution: Mandriva
Posts: 360

Rep: Reputation: 170Reputation: 170
If there is a function called 'read' in the script it will replace the shell builtin and could cause an infinite loop.
Code:
read () {
        # This is a function which always returns true
        return 0
        }

grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '($7 != "'"$which nologin)"'" && $7 != "/bin/false") { print $1 " " $6 }' | while read -r user dir; 
do if [ ! -d "$dir" ]; 
then echo "The home directory ($dir) of user $user does not exist.";
fi;
done;

The home directory () of user  does not exist.
The home directory () of user  does not exist.
The home directory () of user  does not exist.
The home directory () of user  does not exist.
The home directory () of user  does not exist.
The home directory () of user  does not exist.
The home directory () of user  does not exist.
....               # infinite loop

There is a missing parenthesis
Code:
"$which nologin)"
# should be
"$(which nologin)"
 
Old 03-18-2020, 12:00 PM   #12
uncorrupt3d
LQ Newbie
 
Registered: Mar 2020
Location: Pluto (it's a planet)
Distribution: LFS
Posts: 9

Original Poster
Rep: Reputation: Disabled
My test lab vm is a fresh CentOS 8 server install, and my home CentOS 8 vm is the same exact thing. I just tried grail's solution and it didn't work either... I just get the same infinite loop.

It's important to stress that my code already works, even if it's not proper. It's just not working inside CentOS 8 when paired with other bash functions I have defined, which is the intended goal.

I'm throwing it into a function and calling it from another one, because that's what my script needs to do. I can run all this and have it work in the shell, or when I throw this portion of the bash script into it's own script it runs fine- but I cannot invoke it from my main script. That's what I'm trying to understand.

Thanks again for your help! This problem has stumped my coworkers too... Should I try it on a different distro like debian and see if it's just a problem with my code?
 
Old 03-18-2020, 01:08 PM   #13
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,966

Rep: Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332Reputation: 7332
this is not related to the distro, although it may depend on the version of some tools you use (for example bash).
I would suggest you:
1. add set -xv to see what's happening - and try to post some debug output. Probably helps
2. try shellcheck, it can be useful too.
3. also would be nice to prepare a sample script to demonstrate what works and what failed. If we could try it we could also repair it.
 
1 members found this post helpful.
Old 03-18-2020, 01:21 PM   #14
MadeInGermany
Senior Member
 
Registered: Dec 2011
Location: Simplicity
Posts: 2,808

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Here I avoid the problematic which
Code:
# for debugging uncomment the next line
#set -x

while IFS=":" read user pw uid gid gecos dir shell
do
  case $user in (halt|sync|shutdown) continue;; esac
  case $shell in (*/nologin|*/false) continue;; esac
  if [ -n "$dir" ] && [ ! -d "$dir" ]
  then
    echo "The home directory ($dir) of user $user does not exist."
  fi
done < /etc/passwd
As said before, for debugging use set -x or set -vx
 
Old 03-18-2020, 01:25 PM   #15
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,269
Blog Entries: 24

Rep: Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206Reputation: 4206
Quote:
Originally Posted by pan64 View Post
3. also would be nice to prepare a sample script to demonstrate what works and what failed. If we could try it we could also repair it.
This, indeed! It would be very helpful to others, and yourself, to work up a simplest complete test case which demonstrates the problem so that others can reproduce it, better understand the problem and suggest a solution. Descriptions about the problem are not nearly as useful as an example which demonstrates the problem. Preparation of a simplest example often leads you to a better understanding and solution in the process - a valuable troubleshooting methid in itself.

I would suggest you review the Site FAQ for guidance in posting your questions and general forum usage. Especially, read the link in that page, How To Ask Questions The Smart Way. The more effort you put into understanding your problem and framing your questions, the better others can help!
 
  


Reply

Tags
shell



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] sed emits a different output for same inputs in different scripts RandomTroll Linux - Software 8 06-27-2019 11:35 PM
[SOLVED] awk: same test on same file by different scripts gives different result porphyry5 Programming 1 09-30-2012 05:26 AM
ssh: logging in as same user but seeing different behavior quasi3 Linux - Server 2 01-20-2012 10:13 AM
prevent users to run the same script at the same time, on the same machine pvpnguyen Programming 2 09-05-2007 08:52 PM
Alt+<number> - different behavior in different terminals szp Linux - General 0 03-07-2007 03:07 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 09:55 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration