Can't check checksum in bash --- apparently, wrong syntax? - bash

I have this bit of code, that is supposed to call reload if current file ($1) is changed:
thehash="`cksum $1`"
while true
do
curhash="`cksum $1`"
if "$curhash" -ne "$thehash"; then
reload
fi
...
done
tl;dr: it doesn't work.
Since I am not very experienced with bash, I can't figure out what did I do wrong. I get this error:
58003725 834183 main.pdf: command not found
Apparently, bash is trying to execute curhash? How do I fix this?

You need brackets around your condition in if or to use the test command, so it should be
if [[ "$curhash" != "$thehash" ]]; then
and note that -ne is for integer comparison, != is for string comparison
Without the [[ or test the variable gets expanded and that becomes a command to run, which is why it was trying to execute the output of cksum: the content of curhash was being treated as a command.
Also, as #Sundeep mentioned the more often preferred way to get the output from the subshell is to use $(...) instead of the backticks. here is a good answer talking about that

Related

Jenkins Execute shell if then statement fails with `Bad substitution`

I need an if statement in Jenkins Execute Shell, but it always fails on the same line, regardless of what’s there.
I’m trying to do something like this:
if [ " ${BuildVariants[*]} " =~ " VariantA " ]; then
# fails on this line even this line is just a comment
variant_config=""
fi
it fails when I try to assign a variable there, fails when I try to echo "anything", fails even on comment (as example above)
Reason: Bad substitution
Note: There's anything specified in Configure System, so it should be using default Bash.
What the problem might be?
I don't think =~ works inside of [ ... ] -- use [[ ... ]] instead.
Shellcheck is a great tool for find these types of things; it would show that you've hit SC2074.
re:
fails on this line even this line is just a comment
You cannot have an "empty" then block. You can use just : as code to be executed:
if [[ "$foo" == "bar" ]]; then
:
fi
Next idea: get your code to run in a shell script, then put the code up in Jenkins. You will probably need to mock up some of the Jenkins-supplied input to do that, but it takes one more moving part out of the equation. If the code runs from the command line and doesn't in Jenkins, then you need to start looking for Jenkins-specific causes, like maybe it's being run in a csh instead of Bash (I see you mention this specific possibility already, but maybe there's something else like it -- I don't know Jenkins, sorry).
So the problem was that I supposed Jenkins was giving me an array, but it was giving me a string. (I used with Extended Choice Parameter, with multiple choices).
So the condition should've been [[ "$BuildVariants" == *"VariantA"* ]].

Why can't a string variable be evaluated as a command when using ${!variable}?

This is somehow related to Use substituted string as a command in shell script I asked last year. That accepted answer worked nicely.
#!/usr/bin/env bash
for user in ytu
do
cd /home/${user}/H2-Data/crons
for path in "$user"_*_monthly_report.py
do
if [ -e $path ]
then
. ../../.profile
userpython=${user^^}_PYTHON
echo ${!userpython} $path
else
break
fi
done
done
This echos what I expected:
/home/ytu/anaconda3/bin/python ytu_clinic217_monthly_report.py
/home/ytu/anaconda3/bin/python ytu_clinic226_monthly_report.py
However, by simply changing ${user^^}_PYTHON to $YTU_PYTHON, which should be exactly the same in this case, the bash script now echos:
ytu_clinic217_monthly_report.py
ytu_clinic226_monthly_report.py
I even tried userpython=/home/ytu/anaconda3/bin/python but that ends up the same. That said, if I echo $userpython, I can still get /home/ytu/anaconda3/bin/python in the latter cases.
I wonder why can't userpython be evaluated anymore by simply assigning the variable explicitly, and how can I make it right?

Unable to do a IF statement inside a bash heredoc

I am trying to do a IF..ELSE inside a heredoc. The heredoc is necessary because the set of commands need to be executed as a different user.
Unfortunately, the IF statement doesn't work as expected, and always jumps to the ELSE clause. When I remove the IF block from the heredoc and place it outside it works as expected.
It is probably a simple thing I'm missing, but I have no idea what.
rem=0
function1 () {
su - user1 <<'DONE'
if [[ "$rem" -eq 0 ]];
then
echo rem is even
else
echo rem is odd
fi
DONE
}
function1
It echoes rem is odd.
Change the 3rd line of your script with this:
su - user1 <<DONE
Notice the missing quotes around DONE.
With quotes around the delimiter you basically deactivate the parameter substitution and the value of $rem is not what you expect it to be (just echo it and see). Without the quotes, the parameter substitution works.
Tested on a CentOS 7 with Bash 4.2.46.
See also a longer discussion here: Using variables inside a bash heredoc

Echo-ing an environment variable returns string literal rather than environment variable value

I have two bash scripts. The first listens to a pipe "myfifo" for input and executes the input as a command:
fifo_name="myfifo"
[ -p $fifo_name ] || mkfifo $fifo_name;
while true
do
if read line; then
$line
fi
done <"$fifo_name"
The second passes a command 'echo $SET_VAR' to the "myfifo" pipe:
command='echo $SET_VAR'
command_to_pass="echo $command"
$command_to_pass > myfifo
As you can see, I want to pass 'echo $SET_VAR' through the pipe. In the listener process, I've set a $SET_VAR environment variable. I expect the output of the command 'echo $SET_VAR' to be 'var_value,' which is the value of the environment variable SET_VAR.
Running the first (the listener) script in one bash process and then passing a command via the second in another process gives the following result:
$SET_VAR
I expected to "var_value" to be printed. Instead, the string literal $SET_VAR is printed. Why is this the case?
Before I get to the problem you're reporting, I have to point out that your loop won't work. The while true part (without a break somewhere in the loop) will run forever. It'll read the first line from the file, loop, try to read a second line (which fails), loop again, try to read a third line (also fails), loop again, try to read a fourth line, etc... You want the loop to exit as soon as the read command fails, so use this:
while read line
do
# something I'll get to
done <"$fifo_name"
The other problem you're having is that the shell expands variables (i.e. replaces $var with the value of the variable var) partway through the process of parsing a command line, and when it's done that it doesn't go back and re-do the earlier parsing steps. In particular, if the variable's value included something like $SET_VAR it doesn't go back and expand that, since it's just finished the bit where it expands variables. In fact, the only thing it does with the expanded value is split it into "words" (based on whitespace), and expand any filename wildcards it finds -- no variable expansions happen, no quote or escape interpretation, etc.
One possible solution is to tell the shell to run the parsing process twice, with the eval command:
while read line
do
eval "$line"
done <"$fifo_name"
(Note that I used double-quotes around "$line" -- this prevents the word splitting and wildcard expansion I mentioned from happening before eval goes through the normal parsing process. If you think of your original code half-parsing the command in $line, without double-quotes it gets one and a half-parsed, which is weird. Double-quotes suppress that half-parsing stage, so the contents of the variable get parsed exactly once.)
However, this solution comes with a big warning, because eval has a well-deserved reputation as a bug magnet. eval makes it easy to do complex things without quite understanding what's going on, which means you tend to get scripts that work great in testing, then fail incomprehensibly later. And in my experience, when eval looks like the best solution, it probably means you're trying to solve the wrong problem.
So, what're you actually trying to do? If you're just trying to execute the lines coming from the fifo as shell commands, then you can use bash "$fifo_name" to run them in a subshell, or source "$fifo_name" to run them in the current shell.
BTW, the script that feeds the fifo:
command='echo $SET_VAR'
command_to_pass="echo $command"
$command_to_pass > myfifo
Is also a disaster waiting to happen. Putting commands in variables doesn't work very well in the shell (I second chepner's recommendation of BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!), and putting a command to print another command in a variable is just begging for trouble.
bash, by it's nature, reads commands from stdin. You can simply run:
bash < myfifo

BASH: if statement execute command and function

I've run into an issue in which I think should be easy to resolve, but for the life of me, I can't figure it out. Could be that it's really late; not sure.
So I have a shell script and I have an if statement that I need to run. The problem is that I have a function inside this bash script that I am using to actually build part of this find command inside the if statement. I want to know how I can do both, without receiving an error [: too many arguments.
Here's the current code:
if [ -n `find ./ `build_ext_names`` ];then
That's all I really need to post. I need to figure out how to run that build_ext_names inside that find command, which in-turn is inside the ifstatement
Michael Aaron Safyan has the right idea, but to fix the immediate problem you can just use the simpler $(command) construct instead of ```command` `` for command substitution. It allows for much simpler nesting:
if [ -n "$(find ./ "$(build_ext_names)")" ]; then
This is easier if you split it up:
function whateverItIsYouAreTryingToDo() {
local ext_names=$(build_ext_names)
local find_result=$(find ./ $ext_names)
if [ -n "$find_result" ] ; then
# Contents of if...
fi
}

Resources