How to use the --name argument to getopt? - command-line

In the following example, I expected the error message to come from xyz, not from getopt. What am I doing wrong?
/tmp> getopt --name xyz --options "xyz:" -- -x
-x --
/tmp> getopt --name xyz --options "xyz:" -- -x -z
getopt: option requires an argument -- z
-x --
How do I make it say xyz: option requires an argument -- z; isn't that what --name is for?
UPDATE
Seems to be a bug. My getopt comes from cygwin
$ getopt --version
getopt from util-linux 2.25.2

It seems to be bug in some versions of the program.
It works for me in in Centos 7.3 and Fedora 19
[vps1 ~]$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
[vps1 ~]$ getopt --name xyz --options "xyz:" -- -x -z
xyz: option requires an argument -- 'z'
-x --
[vps1 ~]$ getopt -V
getopt from util-linux 2.23.2
But it doesn't in my MinGW shell (from Git for Windows)
$ getopt --name xyz --options "xyz:" -- -x -z
getopt: option requires an argument -- z
-x --
$ getopt -V
getopt from util-linux 2.26.2
Update: It works also in 2.27.1 in Linux. And it doesn't work in (at least some version of) Cygwin. So the problem seems to be in the Windows ports (both Mingw and Cygwin, interestingly).
I'll throw a wild guess (not big probability of hitting the target):
The getopt program, since this commit tries to deal with some environnments (in particular, BSD; not Linux) that have/use the getprogname/setprogname to get/set the "current" program name (instead of relying on argv[0]).
#if defined (HAVE_SETPROGNAME) && !defined (__linux__)
setprogname(name);
Now, let's imagine that
Cygwin and MinGW/Msis both support those functions.
However, they lack the HAVE_SETPROGNAME define
Further, their getopt functions (mind you, not the program), just like the BSD version, use getprogname instead of argv[0]
In that case, the problem would be explained. However, I'm skeptical - of point 3 especially.

This is a bug (or just a non-portability issue) which is already fixed in util-linux 2.28, by commit 30fbf2f6. Before this fix it worked only on Linux, OSX and a few BSD flavors but not on WIN32 or GNU-Hurd for example.
If you can't upgrade util-linux (might be difficult to build on windows), then you could use this shell workaround:
bash -c 'exec -a "XYZ" getopt --options "xyz:" -- -x -z'
Note that still using the --name option would override this trick again once if getopt will be updated one day.
Of course you could also simply copy/link/rename the getopt program to whatever name you want.

Related

How do I send a command to a remote system via ssh with concourse

I have the need to start a java rest server with concourse that lives on an Ubuntu 18.04 machine. The version of concourse my company uses is 5.5.11. The server code is written in Java, so a simple java -jar <uber.jar> suffices from the command line (see below). In production, I will not have this simple luxury, hence my question.
I have an scp command working that copies the .jar from concourse to the target Ubuntu machine:
scp -i /tmp/key.p8 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ./${NEW_DIR}/${ARTIFACT_NAME}.${ARTIFACT_FILE_TYPE} ${SRV_ACCOUNT_USER}#${JAVA_VM_HOST}:/var/www
Note that my private key is passed with -i and I can confirm that is working.
I followed this other SO Q&A that seemed to be promising: Getting ssh to execute a command in the background on target machine
, but after trying a few permutations of the suggested solution and other answers, I still don't have my rest service kicked off.
I've tried a few permutations of this line in my concourse script:
ssh -f -i /tmp/pvt_key1.p8 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${SRV_ACCOUNT_USER}#${JAVA_VM_HOST} "bash -c 'nohup java -jar /var/www/${ARTIFACT_NAME}.${ARTIFACT_FILE_TYPE} -c \"/opt/testcerts/clientkeystore\" -w \"password\" > /dev/null 2>&1 &'"
I've tried with and without the -f and -t switches in ssh, with and without the file stream redirection, with and without nohup and the Linux background ('&') command and various ways to escape the quotes.
At the bash prompt, this line successfully starts my server. The two switches are needed to point to the certificate and provide the password:
java -jar rest-service.jar -c "/opt/certificates/clientkeystore" -w "password"
I really think this is possible to do in Concourse, but I'm stuck at this point.
After a lot of trial an error, it seems I needed to do this:
ssh -f -i /tmp/pvt_key1.p8 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${SRV_ACCOUNT_USER}#${JAVA_VM_HOST} "bash -c 'sudo java -jar /var/www/${ARTIFACT_NAME}.${ARTIFACT_FILE_TYPE} -c \"/path/to/my/certificate\" -w \"password\" > /var/www/log.txt 2>&1 &'"
The key was I was missing the 'sudo' portion of the command. Using nohup as opposed to putting in a Linux bash background indicator ('&') seems to give me an error in the pipeline. This works for me, but others are welcome to post responses with better answers or methods that might be a better practice.

Folder is in $INC but module loads from other location

I need to test my project with new module version.
But for some reason Perl can not load it.
docker-compose run -v /data/projects/My-Module:/perl5lib project-container perl -I/perl5lib -MMy::Module -e 'print $INC{"My/Module.pm"}'
It must print
/perl5lib
But in fact it prints
/usr/local/share/perl/5.26.1/My/Module.pm
Same result with
docker-compose run -e PERL5LIB=/perl5lib -v /data/projects/My-Module:/perl5lib project-container perl -MMy::Module -e 'print $INC{"My/Module.pm"}'
Please help me to find what could be the issue.
$ perl --version
This is perl 5, version 26, subversion 1 (v5.26.1) built for x86_64-linux-gnu-thread-multi
$ docker-compose --version
docker-compose version 1.24.1, build 4667896
With this command (which should be more-or-less equivalent to what you are doing)
docker run perl:5.28 perl -I/xyz -V
Regardless /xyz exists or not within the container instance, I can visually confirm that /xyz is the first entry of #INC:
...
Compiled at Aug 15 2019 02:34:38
#INC:
/xyz
/usr/local/lib/perl5/site_perl/5.28.2/x86_64-linux-gnu
/usr/local/lib/perl5/site_perl/5.28.2
/usr/local/lib/perl5/vendor_perl/5.28.2/x86_64-linux-gnu
/usr/local/lib/perl5/vendor_perl/5.28.2
/usr/local/lib/perl5/5.28.2/x86_64-linux-gnu
/usr/local/lib/perl5/5.28.2
Given by your message that My/Module.pm got required from an alternative place, I'd guess that your /perl5lib is empty, or simply does not contain My/Module.pm -- or somehow that alternative path comes first then your /perl5lib. Not sure how that would be the case, but I'm trying to enumerate some possibilities.
Anyway, you should be able to inspect the content of /perl5lib by running
docker-compose run -v /data/projects/My-Module:/perl5lib project-container find /perl5lib
(I guess find command is available in your container, but if not, try ls -R or something similar...)
Last... in case you are using Docker on macOS -- which I cannot tell from the information you provided -- you need to verify that the /data/projects/My-Module is in the list of "File Sharing" preference. Otherwise that folder cannot be mounted.

How to send data to command line after calling .sh file?

I want to install Anaconda through EasyBuild. EasyBuild is a software to manage software installation on clusters. Anaconda can be installed with sh Anaconda.sh.
However, after running I have to accept the License agreement and give the installation location on the command line by entering <Enter>, yes <Enter>, path/where/to/install/ <Enter>.
Because this has to be installed automatically I want to do the accepting of terms and giving the install location in one line. I tried to do it like this:
sh Anaconda.sh < <(echo) >/dev/null < <(echo yes) >/dev/null \
< <(echo /apps/software/Anaconda/1.8.0-Linux-x86_64/) > test.txt
From the test.txt I can read that the first echo works as <Enter>, but I can't figure out how to accept the License agreement, as it sees it now as not sending yes:
Do you approve the license terms? [yes|no]
[no] >>> The license agreement wasn't approved, aborting installation.
How can I send the yes correctly to the script input?
Edit: Sorry, I missed the part about having to enter more then one thing. You can take a look at writing expect scripts. thegeekstuff.com/2010/10/expect-examples. You may need to install it however.
You could try piping with the following command: yes yes | sh Anaconda.sh. Read the man pages for more information man yes.
Expect is a great way to go and probably the most error proof way. If you know all the questions I think you could do this by just writing a file with the answers in the correct order, one per line and piping it in.
That install script is huge so as long as you can verify you know all the questions you could give this a try.
In my simple tests it works.
I have a test script that looks like this:
#!/bin/sh
echo -n "Do you accept "
read ANS
echo $ANS
echo -n "Install path: "
read ANS
echo $ANS
and an answers file that looks like this:
Y
/usr
Running it like so works... perhaps it will work for your monster install file as well.
cat answers | ./test.sh
Do you accept Y
Install path: /usr
If that doesn't work then the script is likely flushing and you will have to use expect or pexpect.
Good luck!
Actually, I downloaded and looked at the anaconda install script. Looks like it takes command line arguments.
/bin/bash Anaconda-2.2.0-Linux-x86_64.sh -h
usage: Anaconda-2.2.0-Linux-x86_64.sh [options]
Installs Anaconda 2.2.0
-b run install in batch mode (without manual intervention),
it is expected the license terms are agreed upon
-f no error if install prefix already exists
-h print this help message and exit
-p PREFIX install prefix, defaults to /home/cody.stevens/anaconda
Use the -b and -p options...
so use it like so:
/bin/bash Anaconda-2.2.0-Linux-x86_64.sh -b -p /usr
Also of note.. that script explicitly says not to run with '.' or 'sh' but 'bash' so they must have some dependency on a feature of bash.
--
Cody

perl sudo using Net::Openssh not working

I am using salvas' Net::Openssh module, but not able to figure how to use sudo. I have tried the following, but it is not working...
There is nothing printed in results. Single word commands like ls, pwd are also not producing anything.
version of sudo on target system:
$ /usr/local/bin/sudo -V
CU Sudo version 1.5.7p2
$ /usr/local/bin/sudo -h
CU Sudo version 1.5.7p2
usage: /usr/local/bin/sudo -V | -h | -l | -v | -k | -H | [-b] [-p prompt] [-u username/#uid] -s | <command>
since CU sudo does not allow more than 1 option at a time, i supply -k before supplying the command.
please note that this sudo version does not have -S switch to pass password using stdin. so it expects password from terminal. can u pl help more. thx.
$ssh->system("$sudo_path -k");
my #output = $ssh->capture({tty => 1,
stdin_data => "$PASS"},
$sudo_path,
"-p",'', "$cmd");
print " result=#output \n";
OR
$ssh->system("$sudo_path -k");
my #output = $ssh->capture({stdin_data => "$PASS"},
$sudo_path,
"-p",'', "$cmd");
print " result=#output \n";
It would be more helpful if you explained more of what you were trying to accomplish in your question, but I'm assuming you're trying to run a command that requires sudo via ssh using the Net::OpenSSH module in perl.
If that is the case, you should consider trying a 'heredoc' to script a series of commands.
Here is the PerlDoc for Quote-Like operators - look for the area talking about <<EOF as I've often used here docs to script things like this.
If for some reason using a heredoc within the Net::OpenSSH command doesn't work - Net::OpenSSH also works with Expect as documented here.
And if for some reason that doesn't work for you, you could always create a shell script that runs the command with sudo via a heredoc on the remote system, and just execute that script via your Net::OpenSSH connection.

perl run two system commands error

So in my script I need to make to calls to unix, and I do it via the system command like so:
system "bash -i -c 'addmothernode'";
...
perl code ...
...
system "bash -i -c 'addnode -ip=$_'";
However, whenever I run both of these commands in the same script, for some reason my process is stopped like this:
[1]+ Stopped perl boot.pl
And the script can only be finished when I run fg %1. When I only have one of these system calls in, the perl script finishes successfully. But I need both commands because they depend on each other. Anyone have any ideas about what's going on? Thanks!
UPDATE:
A lot of answers below are saying I don't need to use bash -i to run a system command, and I know typically this is true but I need to use aliases that I have created and if I do not use this the aliases won't be recognized. So I do need bash -i.
This problem is unrelated to perl. You can easily reproduce the situation if you start two bashes in the interactive mode (-i) one after another:
$ cat 1.sh
bash -i -c 'sleep 1'
bash -i -c 'sleep 1'
$ bash 1.sh
[1]+ Stopped bash 1.sh
Of course it would be better to run bash in the non-interactive mode (without -i) or run the program directly, without bash, but if you need for some reason bash -i you can protect its run with setsid:
$ cat 1.sh
setsid bash -i -c 'sleep 1'
setsid bash -i -c 'sleep 1'
echo done
$ bash 1.sh
done
The bash -i means run an interactive shell; so you have two shells both reading from the terminal.
Try removing the -i options.
system "addmothernode";
should work.
To execute a command, bash is not needed. The Perl system function is like the system C function, it calls by default sh.
man system
exec
The standard to which the caller conforms determines which shell is used. See standards(5).
Standard Shell Used
______________________________________________________________
1989 ANSI C, 1990 ISO C, 1999 ISO C, /usr/xpg4/bin/sh
POSIX.1 (1990-2001), SUS, SUSv2, SUSv3,
XPG4
POSIX.1 (1988), SVID3, XPG3, no standard /usr/bin/sh
specified