Positional parameter not readable inside the variable - bash script - linux

I have a problem with the small script I am working on, can you please explain why is this not working:
#!/bin/bash
var1=$( linux command to list ldap users | grep "user: $1")
echo $var1
So, when I deploy my script ( ./mycript.sh $michael ), it should use that value instead of $1 and provide the output via echo $variable1? In my case that is not working.
Can you please explain how should I configure positional parameter inside the variable?
I tried the this solution, but that did not help:
#!/bin/bash
var1=$( linux command to list ldap users | grep user: $1)
echo $var1

If you invoke your script as ./mycript.sh $michael and the variable michael is not set in the shell, they you are calling your script with no arguments. Perhaps you meant ./myscript.h michael to pass the literal string michael as the first argument. A good way to protect against this sort of error in your script is to write:
#!/bin/bash
var1=$( linux command to list ldap users | grep "user: ${1:?}")
echo "$var1"
The ${1:?} will expand to $1 if that parameter is non-empty. If it is empty, you'll get an error message.
If you'd like the script to terminate if no values are found by grep, you might want:
var1=$( linux command to list ldap users | grep "user: ${1:?}") || exit
But it's probably easier/better to actually validate the arguments and print an error message. (Personally, I find the error message from ${:?} constructs to bit less than ideal.) Something like:
#!/bin/bash
if test $# -lt 1; then echo 'Missing arguments' >&2; exit 1; fi

Related

Check if script was started by another script [duplicate]

Let's assume I have 3 shell scripts:
script_1.sh
#!/bin/bash
./script_3.sh
script_2.sh
#!/bin/bash
./script_3.sh
the problem is that in script_3.sh I want to know the name of the caller script.
so that I can respond differently to each caller I support
please don't assume I'm asking about $0 cause $0 will echo script_3 every time no matter who is the caller
here is an example input with expected output
./script_1.sh should echo script_1
./script_2.sh should echo script_2
./script_3.sh should echo user_name or root or anything to distinguish between the 3 cases?
Is that possible? and if possible, how can it be done?
this is going to be added to a rm modified script... so when I call rm it do something and when git or any other CLI tool use rm it is not affected by the modification
Based on #user3100381's answer, here's a much simpler command to get the same thing which I believe should be fairly portable:
PARENT_COMMAND=$(ps -o comm= $PPID)
Replace comm= with args= to get the full command line (command + arguments). The = alone is used to suppress the headers.
See: http://pubs.opengroup.org/onlinepubs/009604499/utilities/ps.html
In case you are sourceing instead of calling/executing the script there is no new process forked and thus the solutions with ps won't work reliably.
Use bash built-in caller in that case.
$ cat h.sh
#! /bin/bash
function warn_me() {
echo "$#"
caller
}
$
$ cat g.sh
#!/bin/bash
source h.sh
warn_me "Error: You did not do something"
$
$ . g.sh
Error: You did not do something
g.sh
$
Source
The $PPID variable holds the parent process ID. So you could parse the output from ps to get the command.
#!/bin/bash
PARENT_COMMAND=$(ps $PPID | tail -n 1 | awk "{print \$5}")
Based on #J.L.answer, with more in depth explanations, that works for linux :
cat /proc/$PPID/comm
gives you the name of the command of the parent pid
If you prefer the command with all options, then :
cat /proc/$PPID/cmdline
explanations :
$PPID is defined by the shell, it's the pid of the parent processes
in /proc/, you have some dirs with the pid of each process (linux). Then, if you cat /proc/$PPID/comm, you echo the command name of the PID
Check man proc
Couple of useful files things kept in /proc/$PPID here
/proc/*some_process_id*/exe A symlink to the last executed command under *some_process_id*
/proc/*some_process_id*/cmdline A file containing the last executed command under *some_process_id* and null-byte separated arguments
So a slight simplification.
sed 's/\x0/ /g' "/proc/$PPID/cmdline"
If you have /proc:
$(cat /proc/$PPID/comm)
Declare this:
PARENT_NAME=`ps -ocomm --no-header $PPID`
Thus you'll get a nice variable $PARENT_NAME that holds the parent's name.
You can simply use the command below to avoid calling cut/awk/sed:
ps --no-headers -o command $PPID
If you only want the parent and none of the subsequent processes, you can use:
ps --no-headers -o command $PPID | cut -d' ' -f1
You could pass in a variable to script_3.sh to determine how to respond...
script_1.sh
#!/bin/bash
./script_3.sh script1
script_2.sh
#!/bin/bash
./script_3.sh script2
script_3.sh
#!/bin/bash
if [ $1 == 'script1' ] ; then
echo "we were called from script1!"
elsif [ $1 == 'script2' ] ; then
echo "we were called from script2!"
fi

BASH save stdout to new file upon execution

please bear with me if my terminology or syntax is less than stellar (still learning). I currently have a simple bash script that checks the arguments of the command and outputs files names with matching text. This part of my script works correctly via a grep command and piped to xargs for proper formatting.
When running the script, I run through a simple loop to check if the value is null and then move to running my variable/search if not.
My question is: Is it possible to have this script output via stdout AND also save a new file each time it is run with the user input and date/time? (but not overwrite) EX: report-bob-0729161500.rpt
I saw same other suggestions to use tee with the command, but I was trying to get it to work within the script. Similarly, another suggestion stated to utilize exec > >(tee -i logfile.txt), but I am unsure how to properly format this to include the date/time and $1 input into new files each time the script is executed.
Any help or suggested resources?
Thank you.
SEARCH=`[search_variable]`
if [ -z "$SEARCH" ]
then
echo "$1 not found."
else
echo -e "REPORT LISTING\n\n"
echo "$SEARCH"
fi
EDIT: I did try simply piping the echo statements to the tee command, which does work. However, I am still curious if anyone has other suggestions to accomplish this same task via alternative methods. Thank you.
With echo statements piped to tee:
SEARCH=`[search_variable]`
DATE=`date +"%m%d%y%k%M"`
if [ -z "$SEARCH" ]
then
echo "$1 not found."
else
echo -e "REPORT LISTING\n\n" | tee tps-list-$1-$DATE.rpt
echo "$SEARCH" | tee tps-list-$1-$DATE.rpt
fi
If you want to do it within the script, why then not just write to
both standard output and the file (using append where appropriate?).
Maybe a bit more writing, but it gives complete control.
Leon

BASH grep script

I am trying to work on a bash script that checks for a username in the argument of the script and then outputs the relevant lines from the /etc/passwd and /etc/group files (not the /etc/shadow file). Currently, I am utilizing a if then else loop to check the contents of the /etc/* directory and output the relevant information. My intention was to output simple text line if a match user is not found in the two files, thus a null value. However, it is outputting information that is totally incorrect for what I am looking for.As a new user to BASH, and linux is general, I am sure there are some glaring issues right away. However, I am trying to learn.
Any help with the code of my script or a point in the right direction would be greatly appreciated. Thank you.
#! /bin/bash
USERLOOK='grep -h $USERID ~/etc/* | grep :x:'
grep $1 ~/etc/*
if [ -z $1 ]; then
echo "User not found."
else
echo "$USERLOOK"
fi
exit 0
Finds any lines in /etc/passwd or /etc/group that contain the inputted username:
#!/bin/bash
USERLOOK=$(grep -h "$1" /etc/passwd /etc/group)
if [ -z "$1" ] || [ -z "${USERLOOK}" ]; then
echo "User not found."
else
echo "$USERLOOK"
fi
I want to have the script function where I input ./script
user_to_check. If the username is found, I want to output all lines
where it was found... However, if the username was not found, I wanted
to echo that.
It can be as simple as
#!/bin/bash
grep "^${1}:" /etc/passwd /etc/group
[ $? -ne 0 ] && echo "User : ${1} not found"
As the user name appears in the beginning in both /etc/passwd & /etc/group we placed a ^ in grep to match stuff at beginning and by tradition a : appears just after the username.
Run the script as
./script 'username'
Stop, you're thinking about this all wrong.
A UNIX shell is an environment from which to call UNIX tools with a language to sequence those calls, that is all. The general purpose UNIX tool to manipulate text is awk. So if you need to look for text in a file and have control logic to do anything with it, that should be an awk script, not a shell script. Shells role is to just call awk, something like this:
awk -v user="$1" '
$0 ~ user { line = $0 }
END { print (line != "" ? line : "User not found") }
' /etc/passwd /etc/group
but note that it's trivial with awk to focus the search on just one field, even a different field for each file, unlike how difficult that is in general with grep.

How to read the first line user types into terminal in bash script

I'm trying to write a script where to run the script, the user will type something along the lines of
$./cpc -c test1.txt backup
into the terminal, where ./cpc is to run the script, -c is $option, test1.txt is $source and backup is $destination.
How would I assign the values typed in to the terminal to use them in my script, for example in
if [[ -z $option || -z $source || -z $destination ]]; then
echo "Error: Incorrect number of arguments." (etc)
as when checking the script online the following errors return: 'option/source/destination is referenced but not assigned.'
Sorry in advance if any of this doesn't make sense, I'm trying to be as clear as possible
The arguments are stored in the numbered parameters $1, $2, etc. So, just assign them
option=$1
source=$2
destination=$3
See also man getopt or getopts in man bash.

What does "who | grep $1" command do in the shell script?

I am learning shell programming from the very basics using the book called Beginning Linux Programming (4th Edition). I am confused by this script with an until-clause:
#!/bin/bash
until who | grep "$1" > /dev/null
do
sleep 60
done
# Now ring the bell and announce the unexpected user.
echo -e '\a'
echo "***** $1 has just logged in *****"
exit 0
My quesiton is what is who | grep "$1" > /dev/null used for here? Why redirect the grep output to /dev/null?
The 'until' loop is used to test a condition, as you mentioned, and will run all the 'do|done' block until the condition present becomes true. In other words, it only executes the code block when the condition present is FALSE, and runs it until it becomes true. The script you are testing is useful for catching a logged in user that you pass as a parameter to the script (hence, the grep "$1", being $1 a positional parameter). It will sleep for a minute (sleep 60) until that user logs in to the system, and then it will exit the loop and do all the '$1 has just logged in' stuff. The redirection of grep output to /dev/null is used to not display the output of the grep comand (you could have used grep -q "$1" and that will achieve the same effect).
Hope to have clarified your doubts.
while and until (and, admittedly, if) look at the exit code of the test, not at any text that may or may not be generated on stdout (or stderr).
I suspect the reason redirection to /dev/null has been used is because the command only generates output if there is a match, most of the time there is (admittedly) none, but when there is, you're not interested in seeing the result.

Resources