run the output of a script as a standalone bash command - bash

suppose you have a perl script "foobar.pl" that prints the following to stdout
date -R
and you want to run whatever that perl script outputs as a standalone bash command (don't worry about security problems as this is running in a trusted environment).
How do you get bash to recognize this as a standalone command?
I've tried using xargs, but that seems to want to pass arguments only to a pre-defined command.
I want the perl script to be able to output any arbitrary command.
$command = 'date -R'
system($command); ## in the perl script
the above does not work because I want it to run in an existing cygwin environment ...
foobar.pl | xargs bash -i {}
the above does not work because bash seems to be running a new process and thus the initialization and settings from bash_profile don't get instantiated.

`foobar.pl`

Bad:
`perl foo.pl`
$(perl foo.pl)
Why is this bad? Because of so many reasons; most notably:
Wordsplitting: What you're doing here is taking the output of the perl script, splitting it into chunks wherever there are spaces, tabs or newlines, and taking those chunks as arguments to the first chunk which is the command to run. In really extremely simplistic cases like $(echo 'date +%s') it might work; but that's just a really bad representation of what you're REALLY doing here.
You cannot do quoting or use any other bash shell features like parameter expansion, bash keywords, etc.
Good, but inconvenient:
perl foo.pl > mytmpfile; bash mytmpfile
Creating a temporary file to put your perl script's output into and then running that with bash works, but it's inconvenient as you need to create (and clean up!) your temporary file and have it in a portably writable (and secure!) location.
Also remember not to use . or source to execute the temporary file unless you really intend to run it all in the active shell. Moreover, when you use . or source, you won't be able to reliably clean up your temporary file afterward.
Probably the best solution:
perl foo.pl | bash
This is pretty safe all-round ("safe" in the context of, least bug-prone) assuming your perl script outputs correct bash syntax, of course.
Alternatives that do pretty much the same thing:
bash < <(perl foo.pl)
bash <(perl foo.pl)

Given the perl file:
print "date";
the following bash command will do it.
> $(perl qq.pl)
Mon Apr 6 11:02:07 WAST 2009
But that is run in a separate shell. If you really want to invoke it in the context of the current shell, do this:
$ perl qq.pl >/tmp/qq.$$ ; . /tmp/qq.$$ ; rm -f /tmp/qq.$$
Mon Apr 6 11:04:59 WAST 2009

Try:
foobar.pl | bash

I don't think this is exactly what you're looking for, but its what I've got :-)
perl foo.pl > /tmp/$$.script; bash /tmp/$$.script; rm /tmp/$$.script
Good luck!

Try with open($fh,"-|",$arg1,$arg2)

Related

CMake's execute_process and arbitrary shell scripts

CMake's execute_process command seems to only let you, well, execute a process - not an arbitrary line you could feed a command shell. The thing is, I want to use pipes, file descriptor redirection, etc. - and that does not seem to be possible. The alternative would be very painful for me (I think)...
What should I do?
PS - CMake 2.8 and 3.x answer(s) are interesting.
You can execute any shell script, using your shell's support for taking in a script within a string argument.
Example:
execute_process(
COMMAND bash "-c" "echo -n hello | sed 's/hello/world/;'"
OUTPUT_VARIABLE FOO
)
will result in FOO containing world.
Of course, you would need to escape quotes and backslashes with care. Also remember that running bash would only work on platforms which have bash - i.e. it won't work on Windows.
execute_process command seems to only let you, well, execute a process - not an arbitrary line you could feed a command shell.
Yes, exactly this is written in documentation for that command:
All arguments are passed VERBATIM to the child process. No intermediate shell is used, so shell operators such as > are treated as normal arguments.
I want to use pipes
Different COMMAND within same execute_process invocation are actually piped:
Runs the given sequence of one or more commands with the standard output of each process piped to the standard input of the next.
file descriptor redirection, etc. - and that does not seem to be possible.
For complex things just prepare separate shell script and run it using execute_process. You can pass variables from CMake to this script using its parameters, or with prelimiary configure_file.
I needed to pipe two commands one after the other and actually learned that each COMMAND of the execute_process is piped already. So at least that much is resolved by simply adding commands one after the other:
execute_process(
COMMAND echo "Hello"
COMMAND sed -e 's/H/h/'
OUTPUT_VARIABLE GREETINGS
OUTPUT_STRIP_TRAILING_WHITESPACE)
Now the variable GREETINGS is set to hello.
If you indeed need a lot of file redirection (as you stated), you probably want to write an external script and then execute that script from CMakeLists.txt. It's really difficult to get all the escaping right in CMake.
If you can simplify your scripts to one command generating a file, then another handling that file, etc. then you can always use the INPUT_FILE and OUTPUT_FILE options. Or pass a filename to your command for the input.
It's often much cleaner to handle one file at a time. Although I understand that some commands may need multiple sources and destinations.

getting last executed command from script

I'm trying to get last executed command from command line from a script to be saved for a later reference:
Example:
# echo "Hello World!!!"
> Hello World!!!
# my_script.sh
> echo "Hello World!!!"
and the content of the script would be :
#!/usr/bin/ksh
fc -nl -1 | sed -n 1p
Now as you notices using here ksh and fc is a built in command which if understood correctly should be implemented by any POSIX compatible shells. [I understand that this feature is interactive and that calling same fc again will give different result but this is not the concern do discuss about]
Above works so far so good only if my_script.sh is being called from the shell which is as well ksh, or if calling from bash and changing 1st line of script as #!/bin/bash then it works too and it doesn't if shells are different.
I would like to know if there is any good way to achieve above without being constrained by the shell your script is called from. I understand that fc is a built in command and it works per shell thus most probably my approach is not good at all from what I want to achieve. Any better suggestions?
I actually attempted this, but it cannot be done between different shells consistently.
While
fc -l`,
is the POSIX standard command for showing $SHELL history, implementation details may be different.
At least bash and ksh93 both will report the last command with
fc -n -l -1 -1
However, POSIX does not guarantee that shell history will be carried over to a new instance of the shell, as this requires the presence of a $HISTFILE. If none is
present, the shell may default to $HOME/.sh_history.
However, this history file or Command History List is not portable between different shells.
The POSIX Shell description of the
Command History List says:
When the sh utility is being used interactively, it shall maintain a list of commands
previously entered from the terminal in the file named by the HISTFILE environment
variable. The type, size, and internal format of this file are unspecified.
Emphasis mine
What this means is that for your script needs to know which shell wrote that history.
I tried to use $SHELL -c 'fc -nl -1 -1', but this did not appear to work when $SHELL refers to bash. Calling ksh -c ... from bash actually worked.
The only way I could get this to work is by creating a function.
last_command() { (fc -n -l -1 -1); }
In both ksh and bash, this will give the expected result. Variations of this function can be used to write the result elsewhere. However, it will break whenever it's called
from a different process than the current.
The best you can do is to create these functions and source them into your
interactive shell.
fc is designed to be used interactively. I tested your example on cygwin/bash and the result was different. Even with bash everywhere the fc command didn't work in my case.
I think fc displays the last command of the current shell (here I don't speak about the shell interpretor, but shell as the "process box". So the question is more why it works for you.
I don't think there is a clean way to achieve what you want because (maybe I miss something) you want two different process (bash and your magic command [my_script.sh]) and by default OS ensure isolation between them.
You can rely on what you observe (not portable, depends on the shell interpretor etc.)
You cannot rely on BASH historic because it's in-memory (the file is updated only on exit).
You can use an alias or a function (edited: #Charles Duffy is right). In this case you won't be able to use your "magic command" from another terminal, but for an interactive use it does the job.
Edited:
Or you can provide two commands: one to save and another to look for. In this case you manage your own historic but you have to save explicitly each command that is painful...
So I look for a hook. And I found this other thread : https://superuser.com/questions/175799/does-bash-have-a-hook-that-is-run-before-executing-a-command
# At the beginning of the Shell (.bashrc for example)
save(){ history 1 >>"$HOME"/myHistory ; }
trap 'save' DEBUG
# An example of use
rm -f "$HOME"/myHistory
echo "1 2 3"
cat "$HOME"/myHistory
14 echo "1 2 3"
15 cat "$HOME"/myHistory
But I observe it slows down the interpretor...
Little convoluted, but I was able to use this command to get the most recent command in zsh, bash, ksh, and tcsh on Linux:
history | tail -2 | head -1 | sed -r 's/^[ \t]*[0-9]*[ \t]+([^ \t].*$)/\1/'
Caveats: this uses GNU sed, so you'll need to install that if you're using BSD, OS X, etc; and also, tcsh will display the time of the command before the command itself. Regular csh doesn't seem to having a functioning history command when I tried it.

Execution results are different when I'm executing bash script directly and through sh

I've a file new.sh
i=5
i=$[i+1]
echo $i
when I execute ./new.sh, it shows 6. But when I execute "sh new.sh", it shows
$[i+1]
as output. I just want to know why, and I need a code which will work on both.
Many Linux distributions use dash as their standard shell for scripts and therefore /bin/sh is only a symlink to /bin/dash, which is more lightweight, but lags some functionality compared with bash. Check that with:
ls -l /bin/sh
If you want to write POSIX compatible scripts, you should use foo=$n; $((n=n+1)) instead of foo=$((n++)) and foo=$((n=n+1)) instead of foo=$((++n)). The form $[] is deprecated and should be avoided.
The OS decides what type of file it is based on the first line. Make it:
#!/bin/bash
The behaviour is different because when bash is run as sh, it turns off bash-specific features and hews more closely to the POSIX shell standard.
If you want your code to work the same under both bash and sh, you will need to write your code in the sh subset of the bash syntax.

Bash executing the result of a command

I've got a small script which is returning a string/path. This path is an executable, how can I run the executable? Thank you.
Example:
my_command commands other commands ... returns /home/mydesktop/myexecutable
I'd need to execute /home/mydesktop/myexecutable
You could try this:
`your_command args etc`
The backticks get replaced by the output of the command and that is then evaluated. Since it is at the start of the input line, bash tries to execute it.
This is a handy trick to know, since you can use it for all sorts of fun:
cp your_file .backup/`date "+%Y-%m-%d"`_your_file
will prepend the current date to a copy of your file for poor mans backup...
EDIT: In the comments, we learned, that you should actually be using the $() syntax. So, that amounts to:
$(your_command args etc)
and
cp your_file .backup/$(date "+%Y-%m-%d")_your_file
since you can nest this...
If it returns an executable script/program use:
chmod +x /home/mydesktop/myexecutable
/home/mydesktop/myexecutable
If it returns an executable STRING use:
eval STRING

In bash2, how do I find the name of a script being sourced?

Here's my situation:
I have multiple versions of a script in source code control where the name differs by a path name component (ex: scc/project/1.0/script and scc/project/1.1/script). In another directory, there is a symlink to one of these scripts. The symlink name is not related to the script name, and in some cases may change periodically. That symlink, in turn, is sourced by bash using the '.' command.
What I need to know: how do I determine the directory of the referenced script, on a 10 year-old system with Bash 2 and Perl 5.5? For various reasons, the system must be used, and it cannot be upgraded.
In Bash 3 or above, I use this:
dir=`perl -MCwd=realpath -MFile::Basename 'print dirname(realpath($ARGV[0]))' ${BASH_SOURCE[0]} $0`
Apologies for the Perl one-liner - this was originally a pure-Perl project with a very small amount of shell script glue.
I've been able to work around the fact that the ancient Perl I am using doesn't export "realpath" from Cwd, but unfortunately, Bash 2.03.01 doesn't provide BASH_SOURCE, or anything like it that I've been able to find. As a workaround, I'm providing the path information in a text file that I change manually when I switch branches, but I'd really like to make this figure out which branch I'm using on its own.
Update:
I apologize - apparently, the question as asked is not clear. I don't know in every case what the name of the symlink will be - that's what I'm trying to find out at run time. The script is occasionally executed via the symlink directly, but most often the link is the argument to a "." command running in another script.
Also, $0 is not set appropriately when the script is sourced via ".", which is the entire problem I'm trying to solve. I apologize for bluntness, but no solution that depends entirely upon $0 being set is correct. In the Perl one-liner, I use both BASH_SOURCE and $0 (BASH_SOURCE is only set when the script is sourced via ".", so the one-liner only uses $0 when it's not sourced).
Try using $0 instead of ${BASH_SOURCE[0]}. (No promises; I don't have a bash 2 around.)
$0 has the name of the program/script you are executing.
Is stat ok? something like
stat -c %N $file
bash's cd and pwd builtins have a -P option to resolve symlinks, so:
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
works with bash 2.03
I managed to get information about the porcess sourcing my script using this command:
ps -ef | grep $$
This is not perfect but tells your which is the to process invoking your script. It migth be possible with some formating to determine the exact source.

Resources