LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software
User Name
Password
Linux - Software This forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.

Notices


Reply
  Search this Thread
Old 02-16-2015, 04:02 PM   #1
arunkumar26
LQ Newbie
 
Registered: Feb 2015
Posts: 4

Rep: Reputation: Disabled
Issue in shell scripting


The below coding is to find who are all the user having home directory
for (( a=1; a<=$#; a++ ))
do
ls /home | grep $$a >>/dev/null
if [[ $? -eq 0 ]]
then
echo $$a is having home dir
else
echo $$a is not having home dir
fi
done
Note: Here i am trying to pass usernames as a parameter.
Output:
6712a is having home dir
Issue:
$$a is not taking my parameter value. it is taking $$ as a process ID (6712). I want to check $1, $2, $3, etc using $$a. I tried by using brace like $($a), ${$a}, etc. But it is not working. Kindly help me to resolve this issue.
 
Old 02-16-2015, 04:54 PM   #2
Ser Olmy
Senior Member
 
Registered: Jan 2012
Distribution: Slackware
Posts: 3,298

Rep: Reputation: Disabled
It took me a while to understand what you were trying to accomplish with that for loop, but I think I get it now: You want the loop to iterate though the command line parameters, and then refer to a specific parameter variable, say "$1", inside the loop using a syntax like $${a}, am I right?

The problem is related to the way parameters are expanded. You want "$${a}" to expand to something like "$1" or "$2" initially, with 1 or 2 being the value of $a, and then you want the shell to expand the actual parameter variable ($1 or $2, depending on the value of $a). As you've seen, that's doesn't work.

While some clever coding using delayed expansion might work, may a suggest an alternative approach? How about focusing on $1 and then use shift to iterate through the command line arguments? Just create a loop that runs while $1 is non-null, and inside that loop, check for the existence of a home directory and run shift at the end:
Code:
while ! [ "$1" == "" ]; do
  if [ -d /home/$1 ]; then
    echo $1 has a home directory
  else
    echo $1 does not have a home directory
  fi
  shift
done
(I replaced your "ls dir, pipe to grep, check exit code" test with if [ -d dir ], as that's much less likely to return a false positive.)

This way, you'll be able to handle any number of command line arguments, which I'd argue is an improvement on your initial approach which may be limited to 9, depending on which shell you're using.
 
1 members found this post helpful.
Old 02-16-2015, 05:53 PM   #3
wpeckham
Senior Member
 
Registered: Apr 2010
Location: Continental USA
Distribution: Debian, Ubuntu, RedHat, DSL, Puppy, CentOS, Knoppix, Mint-DE, Sparky, VSIDO, tinycore, Q4OS,Manjaro
Posts: 4,914

Rep: Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400
Picking at nits, again...

It may be worthwhile to note that this only finds users with home folders directly under /home.
I run systems where some sets of users may have home folders in different locations.
Were that an issue, parsing /etc/passwd for accounts with value UID in the user range, and detecting the home folder field, and testing its validity and existance would be a more rigerous solution.

That may be far beyond the need here, just thought I should mention it.
And that even that method could be 'fooled' by some unexpected non-user accounts in high UID space.
 
Old 02-17-2015, 12:04 PM   #4
arunkumar26
LQ Newbie
 
Registered: Feb 2015
Posts: 4

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by Ser Olmy View Post
It took me a while to understand what you were trying to accomplish with that for loop, but I think I get it now: You want the loop to iterate though the command line parameters, and then refer to a specific parameter variable, say "$1", inside the loop using a syntax like $${a}, am I right?

The problem is related to the way parameters are expanded. You want "$${a}" to expand to something like "$1" or "$2" initially, with 1 or 2 being the value of $a, and then you want the shell to expand the actual parameter variable ($1 or $2, depending on the value of $a). As you've seen, that's doesn't work.

While some clever coding using delayed expansion might work, may a suggest an alternative approach? How about focusing on $1 and then use shift to iterate through the command line arguments? Just create a loop that runs while $1 is non-null, and inside that loop, check for the existence of a home directory and run shift at the end:
Code:
while ! [ "$1" == "" ]; do
  if [ -d /home/$1 ]; then
    echo $1 has a home directory
  else
    echo $1 does not have a home directory
  fi
  shift
done
(I replaced your "ls dir, pipe to grep, check exit code" test with if [ -d dir ], as that's much less likely to return a false positive.)

This way, you'll be able to handle any number of command line arguments, which I'd argue is an improvement on your initial approach which may be limited to 9, depending on which shell you're using.
Thank you for your reply. The above coding is to get single username as a parameter. If i want to pass muliple username as a parameter, is there any other possible way without using $$a??
Due to the use of $$, the output is showing me PID.
Actual output:
$$a--> {$$}a --->{6712}a ----> 6712a ---> showing PID plus 'a'
Expecting output
$$a---> ${$a} --->${1} ---> $1 ---> 1st parameter value
it would be helpful for me, if you let me know the proper syntax to get above result.
Thank you!!
 
Old 02-17-2015, 12:13 PM   #5
arunkumar26
LQ Newbie
 
Registered: Feb 2015
Posts: 4

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by wpeckham View Post
It may be worthwhile to note that this only finds users with home folders directly under /home.
I run systems where some sets of users may have home folders in different locations.
Were that an issue, parsing /etc/passwd for accounts with value UID in the user range, and detecting the home folder field, and testing its validity and existance would be a more rigerous solution.

That may be far beyond the need here, just thought I should mention it.
And that even that method could be 'fooled' by some unexpected non-user accounts in high UID space.
Yes. it is great idea. I understand that home directory may present in different location too. Grepping home directory from /etc/passwd is a best way.
 
Old 02-17-2015, 06:25 PM   #6
Ser Olmy
Senior Member
 
Registered: Jan 2012
Distribution: Slackware
Posts: 3,298

Rep: Reputation: Disabled
Quote:
Originally Posted by arunkumar26 View Post
Thank you for your reply. The above coding is to get single username as a parameter. If i want to pass muliple username as a parameter, is there any other possible way without using $$a??
Did you try the code I posted? It does support multiple user accounts as arguments. In fact, it supports any number of arguments, unlike your approach which may be limited to 9 arguments ($1 - $9).
Quote:
Originally Posted by arunkumar26 View Post
Due to the use of $$, the output is showing me PID.
Actual output:
$$a--> {$$}a --->{6712}a ----> 6712a ---> showing PID plus 'a'
Expecting output
$$a---> ${$a} --->${1} ---> $1 ---> 1st parameter value
it would be helpful for me, if you let me know the proper syntax to get above result.
I attempted to explain why this isn't working: You're expecting the shell to perform variable expansion twice, once from "$${a}" to "$1" (or whichever number $a may contain), and then again from "$1" to the contents of the $1 variable. The shell just doesn't expand variables recursively like that.

You can code around it with eval if you like:

eval echo \$${a} ---> eval echo $1 ---> echo (1st parameter value)

The first expansion returns a literal $ character (due to the backslash escape) followed by the contents of the variable $a. Then echo $1 is evaluated, returning the contents of $1.

In general, using eval is not a recommended practice since it basically just runs whatever you pass it. If you make a simple mistake or a malicious user is able to manipulate the input to embed a command, you may end up executing said command and compromise the system. Anyway, here's how it might work:
Code:
for (( a=1; a<=$#; a++ )) do
  tempvar=\$$a
  user_acct=$(eval echo $tempvar)
  if [ -d /home/$user_acct ]; then
    echo $user_acct has a home directory
  else
    echo $user_acct does not have a home directory
  fi
done
(Thought experiment: Imagine what would happen in the above script if the $1 variable somehow contained the text "foo ; dd if=/dev/zero of=/dev/sda". eval is potentially very dangerous.)

As for locating a user's home directory by searching through /etc/passwd, that may not be such a good idea. It will work on most standalone systems, but will fail if the user account is stored in a database.

A (much) better approach would be to parse the output from getent passwd. It returns user information in exactly the same format as used by the /etc/passwd file, but it fetches that information from whichever source the system is configured to use, be that /etc/passwd, NIS, Active Directory/winbind, an LDAP server, or something else entirely.

Here's a version of my code using getent:
Code:
while ! [ "$1" == "" ]; do
  home_dir=$(getent passwd | grep ^$1\: | cut -d : -f 6)
  if [ ! "$home_dir" == "" ] && [ -d $home_dir ] ; then
    echo $1 has a home directory
  else
    echo $1 does not have a home directory
  fi
  shift
done
And here's a (horribly insecure) version that expands variables recursively using eval:
Code:
for (( a=1; a<=$#; a++ )) do
  tempvar=\$$a
  user_acct=$(eval echo $tempvar)
  home_dir=$(getent passwd | grep ^$user_acct\: | cut -d : -f 6)
  if [ ! "$home_dir" == "" ] && [ -d $home_dir ]; then
    echo $user_acct has a home directory
  else
    echo $user_acct does not have a home directory
  fi
done
Note that since grepping the output from the getent command (or the /etc/passwd file for that matter) will return an empty string if the user either doesn't exist or doesn't have a defined home directory, an extra test is required to check for a blank directory variable. For some reason, if [ -d $var ] evaluates to "true" if $var is blank.
 
1 members found this post helpful.
Old 02-17-2015, 09:09 PM   #7
wpeckham
Senior Member
 
Registered: Apr 2010
Location: Continental USA
Distribution: Debian, Ubuntu, RedHat, DSL, Puppy, CentOS, Knoppix, Mint-DE, Sparky, VSIDO, tinycore, Q4OS,Manjaro
Posts: 4,914

Rep: Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400Reputation: 2400
nice!

Listen to Ser Olmy, he has excellent taste.
I was wondering about that use of grep though, I think another solution may serve better: using the above solution without eval but in my style.

Code:
while [[ -n "$1" ]]; do
  if id ${1} >/dev/null 2>&1
  then
     home_dir=$(getent passwd $1 | cut -d: -f6)
     if [[ -n "$home_dir" ]] && [[ -d $home_dir ]] ; then
        echo "${1} has a home directory ${home_dir}"
     else
        echo "${1} does not have a home directory."
     fi
  else
     echo "${1} is not a user"
  fi
  shift
done
Since getent with a parameter only outputs for that parameter, the disk access become avoidable.
I added a test for real users simply because I cannot leave well enough alone. Ever. And Ser Olmy may know a better way.
 
Old 02-18-2015, 08:26 AM   #8
Keith Hedger
Senior Member
 
Registered: Jun 2010
Location: Wiltshire, UK
Distribution: Linux From Scratch, Slackware64, Partedmagic
Posts: 3,067

Rep: Reputation: 832Reputation: 832Reputation: 832Reputation: 832Reputation: 832Reputation: 832Reputation: 832
To use the value of a bash variable to point to another variable you don't need to use eval you can do sort of 'c' type pointer like so:
Code:
AVAR="some text"
ANOTHERVAR="more text"
POINTERVAR="AVAR"
echo $AVAR
some text
echo $POINTERVAR
AVAR
echo ${!POINTERVAR}
some text
POINTERVAR="ANOTHERVAR"
echo ${!POINTERVAR}
more text
This is read only you can't asign values with "!" but it does avoid at least one use of eval, this is not POSIX complient ( I think ) so if you need to use this with a different shell than BASH you will have to check that it works on that shell.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

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] issue in shell scripting with multiple ifs lapishater276 Debian 2 07-17-2014 08:31 PM
win32,shell code,shell programming,shell scripting? mr.cracker Linux - Newbie 4 07-12-2013 11:20 PM
shell scripting issue izual Linux - Newbie 11 01-15-2012 11:01 AM
Shell Scripting text file creation issue AiresTheBold Linux - Newbie 1 02-16-2011 09:27 AM
shell scripting: "formatting" issue with text files eur0dad Programming 3 08-17-2006 10:50 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Software

All times are GMT -5. The time now is 06:23 PM.

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