How to get version number and then compare it to minimum - bash

I am using this answer to compare the min version number that is required. But before i go to comparison, I am actually stuck on how to extract the version number.
My current script looks like this
#!/usr/bin/env bash
x=`pgsync -v`
echo "---"
echo $x
and its output is
> ./version-test.sh
0.6.7
---
I have also tried with x="$(pgsync -v)" and i am still getting an empty string. What am i doing wrong here.

If you're trying to capture a command's output in a variable and it's instead getting printed to the terminal, that's a sign the command isn't writing to its standard output, but to another stream - usually standard error. So just redirect it:
x=$(pgsync -v 2>&1)
As an aside, writing out an explicitly requested version number to standard error instead of standard output is counter intuitive and arguably a bug.
Also, prefer $() command substitution to backticks; see Bash FAQ 082 for details.

Related

bash race condition using generated output file name

I am trying to generate output using a randomized file name. "Generating" is simulated with "cat" in this example:
cat report.csv > "test_$(openssl rand -base64 102).csv"
I often get an error like this:
-bash: test_Q6eheRaVfktCTCfWSU/tjRNA1y+6juwlyuo1lEId/7HZTCQIE7/rt+/9MlTI+pjT
9It3l7FtBldMmaqHNWpspwCI5kCpR+s51RA2o9xAZ6BrZ+7UBR5atK9qWdSO/N/X
BAnvDkGm.csv: No such file or directory
The probability for this error is higher the higher the number of random characters is, which suggests a race condition. Solving the problem by using a variable for the random characters is obvious and is not what I am asking. Rather, my question is: What are the individual steps that bash performs, and where is the race condition?
I would have thought that bash executes the command as follows:
Create a pipe to capture the output of openssl rand
fork/exec openssl rand, passing that file handle as stdout, and wait for the process to finish (and check error status)
read from the pipe to get the value used in string interpolation, then close the pipe
perform string interpolation to build the output file name
open the output file
fork/exec cat, passing the handle for the output file as stdout
wait for the process to finish (and check error status), then close the output file
Nothing here suggests a race condition. I could imagine that bash instead runs cat in parallel and opens another pipe to buffer its output before it goes into the output file, but that wouldn't cause a "No such file or directory" either.
As was commented, slashes in the filename are an obvious problem, but the error occurs even without slashes. Setting the number of random bytes to 8 sometimes produces errors like this, without a slash and with the correct number of characters (so no slash was hidden):
-bash: test_9od1IhDt5A4=.csv: No such file or directory
The following command waits 2 seconds, then runs the command. In exactly those cases where the strange error message appears, it waits 4 seconds instead. Is there some kind of repeat login in bash that does this?
cat report.csv > "test_$(sleep 2; openssl rand -base64 9).csv"
Confirmed the double execution by echoing to stderr instead of sleeping:
cat report.csv > "test_$(echo foo 1>&2; openssl rand -base64 9).csv"
Several things are happening here.
The key part is that an error in the command substitution causes it to be evaluated twice. This seems to be a bug in the bash version used by Apple. The changelog at https://github.com/bminor/bash/blob/master/CHANGES says for version bash-4.3-alpha: "Fixed a bug that could result in double evaluation of command substitutions when they appear in failed redirections." I ran my tests on "GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)", the version pre-installed on macOS.
Steps to reproduce this bug in a simple way:
lap47:~/Documents> echo foo > "test_$(echo bar 1>&2; echo 'foo')"
bar
lap47:~/Documents> echo foo > "test_$(echo bar 1>&2; echo 'f/oo')"
bar
bar
-bash: test_f/oo: No such file or directory
lap47:~/Documents>
The second important part is that evaluating the command substitution twice invokes openssl rand twice, producing different random numbers. The second invocation seems to be used to generate the error message after the redirection failed. This way, the error message does not reflect the condition that caused the error.
Finally, the root cause of the failed redirection is indeed a slash in the file name. This failure causes the substitution to be invoked again, producing a different file name which somewhat likely doesn't contain a slash.

Return results for Ubuntu Updates in a Variable

I am trying to get the result from /usr/lib/update-notifier/apt-check on a Ubuntu 16 Server into a Array to make a XML response for a monitoring tool, but somehow the value of this apt-check just refuses to get in my Variable. For simplicity sake, I have omitted the XML creation part.
#!/bin/bash
APTCHECK="/usr/lib/update-notifier/apt-check"
APTResult="$(${APTCHECK})"
echo "Result is $APTResult"
exit 0
if you now run this code with bash -x you will see that the result is returned to the Terminal, but not assigned to the Variable. If I substitute the "command" to something simple like "ls -lah" everything works fine.
I just don't know why this is not working ? Anybody ?
apt-check prints to the stderr, so you need to capture that instead with aptresult=$(/usr/lib/update-notifier/apt-check 2>&1).
The other option is with the --human-readable switch, which'll print to the stdout. The only problem then is that you have to parse the text output (unless the text output is what you actually want).

using sleep and clear command in bash script

I am learning Bash script.
Inside my script, I've tried clear and sleep 1.
sleep 1 runs as expected but clear runs with command not found error.
Why is that?
clear and sleep 1 are enclosed within backslashes in my script. Stack Overflow is using those symbols to indicate code, and I don't know how to use escape symbols to type them here.
I assume you are trying this :
$(clear)
Or this
`clear`
These are two ways of expression command substitution. The first one is more readable and should be preferred, but they do the same thing.
The solution is to simply use this:
clear
Now, if you are interested in understanding why you are getting this error, here is a longer explanation.
Command substitution captures the output of the command enclosed -- the output of standard output (file descriptor 1), not standard error (standard error), to be more precise -- and then provides this output as a string to be used as part of the command where it was found, as if it had been part of the command to start with (but not subject to further expansions).
The clear command has a special output that causes the terminal screen to clear. But by enclosing clear in backticks, this special output is not sent to the terminal (the terminal does not clear), and it is instead captured by the command substitution. Then, this special output is provided as if it had been typed on the command line, and since it is the first (and only) thing on that line, the shell tries to find a command with a name equal to that special character, which it does not find, and that is where you get the "command not found" error.
Just for fun, try this :
$(clear >&2)
It will clear the screen, and not trigger an error, because the output is redirected to file descriptor 2 (standard error), which is not captured by the command substitution and actually is sent to the terminal (which clears), and since there is no other output, the command substitution evaluates to an empty string, which Bash interprets as a request to do nothing (Bash does not try to find a command with a zero-length name).
I hope this helps you understand the reason you are getting this error.
The backquotes are used in Stack Overflow indicating the a string should be shown as code. Your real code doesn't need backquotes:
echo "Hello from commandline"
sleep 3
echo "After sleeping, will try to use clear in 4 seconds"
sleep 1
echo "will try to use clear in 3 seconds"
sleep 1
echo "will try to use clear in 2 seconds"
sleep 1
echo "will try to use clear in 1 seconds"
sleep 1
clear
When you have seen backquotes in Unix code, you see an old fashioned way to call another command during the execution of a command.
echo "Current time is `date`, so hurry!"
Now we write this as
echo "Current time is $(date), so hurry!"
It is one char more in this simple case, but much better when nesting more things. I will not even try to write the next example with backquotes
echo "Previous command returned $(echo "Current time is $(date), so hurry!")."

Testing for pipe or console standard input with ARGF

I have written a script that I would like to take input either from a pipe or by providing a filename as an argument. ARGF makes it easy to deal with this flexibly, except in the incorrect usage cases where neither is provided, in which case STDIN is opened and it hangs until the user inputs something on the console.
I would like to catch this incorrect usage to display an error message and exit the program, but I haven't been able found a way. ARGF.eof? seemed like a possible candidate, but it also hangs until some input is received.
Is there a simple way for Ruby to discriminate between STDIN provided by a pipe and one from the console?
you can use
$stdin.tty?
for example
$ ruby -e 'puts $stdin.tty?'
> true
$ echo "hello" | ruby -e 'puts $stdin.tty?'
> false

Bash script store command output into variable

I have a problem concerning storing the output of a command inside a variable within a bash script.
I know in general there are two ways to do this
either
foo=$(bar)
# or
foo=`bar`
but for the Java version query, this doesn't seem to work.
I did:
version=$(java --version)
This doesn't store the value inside the var. It even still prints it, which really shouldn't be the case.
I also tried redirecting output to a file but this also fails.
version=$(java -version 2>&1)
The version param only takes one dash, and if you redirect stderr, which is, where the message is written to, you'll get the desired result.
As a sidenote, using two dashes is an inofficial standard on Unix like systems, but since Java tries to be almost identical over different platforms, it violates the Unix/Linux-expectations and behaves the same in this regard as on windows, and as I suspect, on Mac OS.
That is because java -version writes to stderr and not stdout. You should use:
version=$(java -version 2>&1)
In order to redirect stderr to stdout.
You can see it by running the following 2 commands:
java -version > /dev/null
java -version 2> /dev/null

Resources