About saving value of expression in shell programming - linux

I'm just so stressed struggling about this simple grammar.
I learned in text;
To store value in shell programming, we use value=$(expression).
So I made following script
#!/bin/bash
address=$1
echo $address
value=$(test -d $address)
echo $value
This is a script to find if my input(directory) exists and accessible.
Address shows me the input but $value shows nothing.
At least I expected 0 or non-zero but it didn't!
Can anybody teach me how to save the result of test?

test has no output, but the result of the last command is saved as $?, so you could either -
value=$(test -d $address; echo $?)
or just skip saving it and use the test.
if [ -d "$address" ]
then ...

Related

Failure of bash script called via command substitution does not stop parent script

I have a bash script (exp1.sh)
#!/bin/bash
set -e
for row in $(./exp2.sh);
do
echo $?
echo outer=$row
done
echo "continuing"
which invokes another bash script exp2.sh.
#!/bin/bash
echo "A"
echo "B"
exit 1
I want the first script to fail fast when second script exits with error. (The second script actually reads rows from database to stdout, in case of database connectivity error it returns nonzero exit code.)
I expect the set -e option causes premature termination of first script exp1.sh. However from script output it seems that even the exitcode from second script is passed to first script the loop is performed and script continues beyond the loop:
1
outer=A
0
outer=B
continuing
I want neither the loop nor any command after loop to be executed. I understand the second script had passed some data to first script before it exited with error so loop processed them. I don't understand why loop didn't stop then and what's the correct fix.
The best thing I could figure out is to store result of command substitution into array, which works.
a=$(./exp2.sh)
# execution won't get here when error
echo $?
for row in $a
Is there a way to do this without anything being executed? I played with inherit_errexit as I found here but with no success.
Your idea for a solution is good but a=$(./exp2.sh) doesn't populate an array, it populates a string and then for row in $a is leaving the contents of that string unquoted and so open to the shell for interpretation. You can do this to make/use a as an array if the output of exp2.sh is as simple as you show:
a=( $( ./exp2.sh ) )
(( $? == 0 )) || exit 1
echo "$?"
for row in "${a[#]}"
but rather than a=( $(./exp2.sh) ) which has some caveats, it'd be more robust to do:
IFS=$'\n' read -r -d '' -a a < <( ./exp2.sh && printf '\0' )
or:
readarray -t a < <( ./exp2.sh )
See Reading output of a command into an array in Bash and How to split a string into an array in Bash?

for what reason do i keep getting "bsd syntax error" when i run ps -f in shell script?

so, i'm a beginner to Shell, i need to write a script that, given a numeric argument, will display info about the processes with the entered numbers (by user ofc), given a non numeric argument, if it's "--help" it will display help (which i really don't know how to do), and if it's something else, it gives info about processes having that argument as names ( for ex, "./script bash" will display info about the bash process) , here is what i've done so far:
#!/bin/sh
case $arg in
$num )
a=""
for arg in $#
do
a="${a} $arg"
done
printf "$(ps -f ${a})\n"
;;
*)
p=$(pidof ${arg})
printf "$(ps -f ${p})"
;;
esac
so, the numeric argument part works, but the other one doesn't, whenever i enter a non numeric name i get that "BSD syntax" error message and i really don't know what to do.
thank you to everyone willing to help me out.

Bash Read CSV value from Variable in Bash

So in a script I have a variable that changes each time the script is run. The variable is IP_PING. It is basically a list of if address seperated by commas.
I want the script to take each ip addess in the csv variable and ping it. The example below works fine if I only have one ip address in the variable.
IP_PING="192.168.1.1"
echo "$IP_PING" | while read -r -d, ip1
do
ping -c 4 "$ip1" >/dev/null
if [ $? -ne 0 ]; then
echo "Ping fail"
else
echo "Ping success"
fi
done
But if I change the IP_Ping variable to = "192.168.1.1,192.168.100.1" The script only reads the first value 192.168.1.1. Is "while read" the wrong command to use for this. It is reading each value as a seperate column. I want all the values in a single column and then do the while on each value. If that makes sense.
"192.168.1.1,192.168.100.1"
Here is your issue. The problem is that when the while loop is reading, after the first iteration there is no more commas so it hits the end and quits. "192.168.1.1,192.168.100.1," should show the way you are intending.
As other's have suggested in the comments, I would go with a for loop myself, but to with closet to how you were trying to implement it, this would work for you:
IP_PING="192.168.1.1,192.168.1.2,192.168.1.3"
while read -r ip1 ; do
ping -c 4 "$ip1" >/dev/null
if [ $? -ne 0 ]; then
echo "Ping fail"
else
echo "Ping success"
fi
done < <(echo $IP_PING |tr ',' '\n')
< <(echo $IP_PING |tr ',' '\n') is process substitution. To put it simply it will "pipe" the output into the while loop. The difference from a normal pipe being that it creates one less process; the actual pipe creates a process itself.
Using process substitution will also keep the while loop in the current environment as well. (vs a while loop usually takes a 'snapshot' of the env and uses that during the loop, then it disappears when the loop ends) Meaning, with process substitution, that processes in the while loop will be able to see changes in env variables of the parent while it is looping, AND variables assigned within the loop will also still be available in the parent after it ends.

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

Confused about use of return status code shell script?

In a book I'm reading the below line
ls "$1" 2>/dev/null | grep "$1" 2>/dev/null 1>&2
when written in a script - by the book it says "The command is executed to check whether the file passed as the command line argument exists. The standard error is redirected to /dev/null (the unix black hole), and standard output is redirected to standard error by using 1>&2. Thus, the command does not produce any output or error message; its only puprose is to set the command returns status value $?."
But running the code:
if [ $? -eq 0 ]
would I not know it otherwise, I have tried without the cmd at beginning and with it as well with having no impact on the results. I'm sure the author would have written for some purpose. I cannot just figure what?
This looks like a very bad book, giving code that noone sane would ever write to poorly illustrate concepts that are generally used in completely different ways in shell scripts.
The line:
ls "$1" 2>/dev/null | grep "$1" 2>/dev/null 1>&2
is as described -- it has no visible effect other than setting the return code. Is your question about what this does in detail to get a return code or something else?
The line:
if [ $? -eq 0 ]
is an incomplete fragment that checks the return code of the previous command. It's incomplete as there is no then or fi, without which the shell will reject it as a syntax error and not do anything (if you type the above at a prompt, you'll get the secondary prompt, telling you the shell is waiting for more input to get a complete command). So without more code there's no apparent effect. Something more complete like:
if [ $? -eq 0 ]; then echo YES; else echo NO; fi
would output YES or NO based on that return code.
A more sensible way of doing the 6 lines starting with the ls would be:
if [ ! -e "$1" ]; then
echo "$1: not found"
exit 1
fi
As to what the ls line actually does, it runs ls (list files) with the name in $1 as an argument, then uses grep to search that listing for the same filename.
So if the file does not exist, ls gives an error and outputs nothing, so the grep fails (setting $? to 1). If the filename exists and is not a directory, the grep will succeed (setting $? to 0). Finally, if the filename exists and is a directory, it will search the contents of that directory, looking for any file or subdirectory with the same name as a substring -- which is probably just a bug. In addition, if $1 is a string beginning with -, it will do something fairly useless and unpredictable.
Overall, a prime example of a shell script that should never be written -- any student that turned in such a monstrosity should get an immediate F.

Resources