I've seen some shell scripts in which they pass a file by writing the contents of the file in the same shell script. For instance:
if [[ $uponly -eq 1 ]]; then
mysql $tabular -h database1 -u usertrack -pabulafia usertrack << END
select host from host_status where host like 'ld%' and status = 'up';
END
exit 0
fi
I've been able to do something similar in which I do:
python << END
print 'hello world'
END
If the name of the script is say myscript.sh then I can run it by executing sh myscript.sh. and I obtain my desired output.
Question is, can we do something similar in a Makefile? I've been looking around and they all say that I have do something like:
target:
python #<<
print 'hello world'
<<
But that doesn't work.
Here are the links where I've been looking:
http://www.opussoftware.com/tutorial/TutMakefile.htm#Response%20Files
http://www.scribd.com/doc/2369245/Makefile-Memo
You can do something like this:
define TMP_PYTHON_PROG
print 'hello'
print 'world'
endef
export TMP_PYTHON_PROG
target:
#python -c "$$TMP_PYTHON_PROG"
First, you're defining a multi line variable with define and endef. Then you need to export it to the shell otherwise it will treat each new line as a new command. Then you reinsert the shell variable using $$.
The reason your #<< thing didn't work is that it appears to be a feature of a non-standard make variant. Similarly, the define command that mVChr mentions is specific (as far as I'm aware) to GNU Make. While GNU Make is very widely distributed, this trick won't work in a BSD make, nor in a POSIX-only make.
I feel it's good, as a general principle, to keep makefiles as portable as possible; and if you're writing a Makefile in the context of an autoconf-ed system, it's more important still.
A fully portable technique for doing what you're looking for is:
target:
{ echo "print 'First line'"; echo "print 'second'"; } | python
or equivalently, if you want to lay things out a bit more tidily:
target:
{ echo "print 'First line'"; \
echo "print 'second'"; \
} | python
Related
When I'm working on a bash script and need to write a particularly complex logic I usually fall back on using python, like this:
#!/bin/bash
function foo() {
python << END
if 1:
print "hello"
END
}
foo
How can I do the same thing from within a Makefile?
You may write a bash script containing your functions, say myscript.sh:
#!/bin/bash
foo() {
python << END
if 1:
print "hello $1"
END
}
Now here is a Makefile:
SHELL = /bin/bash
mytarget ::
#source myscript.sh ;\
foo world
Finally type in your terminal:
$ make mytarget
hello world
Some explanations on the Makefile: defining SHELL let make know which shell to run. The :: stands for phony target (and a little more); you can replace it with : for an actual target.
The key point is to run source and call the function in the same shell, that is, in the same line (since make run a different shell for each line); this is achieved by ;\ at the end of each line.
I'm trying to customize my zsh prompt. The function below calls a Python script and returns the entire working directory path minus just the current directory. E.g. ~/research would go to ~. This is a .zsh-theme file.
function collapse_pwd {
echo $(python ~/.oh-my-zsh/themes/truncatecwd.py '%~' '%c')
}
This is the python script, truncatecwd.py.
#!/usr/bin/env python
import sys
cwd = sys.argv[1]
current_dir_end = sys.argv[2]
sys.stdout.write(cwd[0: cwd.index(current_dir_end)])
Weird things happen here. I keep getting errors saying that current_dir_end can't be found in cwd. I think that it has something to do with string formatting. I printed out cwd, and it seems to be correct: '~/.oh-my-zsh/themes'. However, when I call length on it, I get 2. Same goes for current_dir_end: I get length 2. In fact, even cwd = '~' returns a length of 2. Clearly, something subtle (but probably simple) is going on.
Thanks for your help.
I don't really understand what you're trying to do here, but wouldn't the following suffice, with no Python involved at all?
collapse_pwd() {
local result=${1:-$PWD}
if [[ $result = */* ]]; then
result="${result%/*}"
fi
if [[ $result = "$HOME"/* ]]; then
result="~/${result#$HOME/}"
fi
echo "$result"
}
could you do something like this:
import os
import sys
cwd = os.getcwd()
ret = os.path.sep.join(cwd.split(os.path.sep)[:-1])
sys.stdout.write(ret)
also, just an observation, because I'm not too familiar with zsh you may need to call python with -u option to ensure unbuffered output otherwise a newline may be written and that wouldn't be good with a command prompt.
I have a Python script, which checks my language translation files (in CSV format), if all the lines contain translations for all languages included in the CSV's first, header line. Script lists files/lines with missing translations. If no problem is found, it outputs OK.
My question is following:
how do I call the script from within the makefile and check, if the output was OK? If something else than OK was printed out by the script, I want the makefile to stop.
Any ideas?
make checks output status, not text which is output. The simplest solution is to use sys.exit(1) in your python script if it doesn't check out OK.
for example:
targetfile: dependsonfile
python pythonscript.py -o targetfile dependsonfile
Of course the actual syntax will depend critically on how pythonscript.py is meant to be called.
If your pythonscript just does a check, you can accomplish that as follows:
makethis: didcheck
echo "made it" > makethis
didcheck: #dependencies here
python -c 'import sys; sys.exit(1)'
touch didcheck
then you call make as make makethis.
If modifying the Python script so that it would indicate its result using an exit code is not possible, you can implement it in Make as follows (assuming you work on *nix):
check-i18n:
#if [ `python your_script.py` = 'OK' ]; \
then echo "good enough"; \
else echo "too bad"; exit 1; \
fi
Another option, less readable, but cross-platform (so it will work on Windows as well):
# String equality check.
eq = $(findstring $1,$(findstring $2,$1))
check-i18n:
#$(if $(call eq,OK,$(shell python your_script.py)), \
echo "good enough", \
echo "too bad"; exit 1)
as my answer to this question
import os
import copy
import subprocess
def command(command):
env = copy.deepcopy(os.environ)
proc = subprocess.Popen(command,
shell=True, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result = proc.stdout.read()
return result
ret = command(YOUR COMMAND HERE)
if ret == "OK":
print "yay"
else:
print "oh no!: %s" % ret
if you showed your code i could better implement my answer into it.
What I'd like to do is something like
$echo $PATH | python --remain-interactive "x = raw_input().split(':')"
>>>
>>> print x
['/usr/local/bin', '/usr/bin', '/bin']
I suppose ipython solution would be best. If this isn't achievable, what would be your solution for the situation where I want to process output from various other commands? I've used subprocess before to do it when I was desperate, but it is not ideal.
UPDATE: So this is getting closer to the end result:
echo $PATH > /tmp/stdout.txt; ipython -i -c 'stdout = open("/tmp/stdout.txt").read()'
Now how can we go about bending this into a form
echo $PATH | pyout
where pyout is the "magic solution to all my problems". It could be a shell script that writes the piped output and then runs the ipython. Everything done fails for the same reasons bp says.
In IPython you can do this
x = !echo $$$$PATH
The double escape of $ is a pain though
You could do this I guess
PATH="$PATH"
x = !echo $PATH
x[0].split(":")
The --remain-interactive switch you are looking for is -i. You also can use the -c switch to specify the command to execute, such as __import__("sys").stdin.read().split(":"). So what you would try is: (do not forget about escaping strings!)
echo $PATH | python -i -c x = __import__(\"sys\").stdin.read().split(\":\")
However, this is all that will be displayed:
>>>
So why doesn't it work? Because you are piping. The python intepreter is trying to interactively read commands from the same sys.stdin you are reading arguments from. Since echo is done executing, sys.stdin is closed and no further input can happen.
For the same reason, something like:
echo $PATH > spam
python -i -c x = __import__(\"sys\").stdin.read().split(\":\") < spam
...will fail.
What I would do is:
echo $PATH > spam.bar
python -i my_app.py spam.bar
After all, open("spam.bar") is a file object just like sys.stdin is :)
Due to the Python axiom of "There should be one - and preferably only one - obvious way to do it" I'm reasonably sure that there won't be a better way to interact with other processes than the subprocess module.
It might help if you could say why something like the following "is not ideal":
>>> process = subprocess.Popen(['cmd', '/c', 'echo %PATH%'], stdout=subprocess.PIPE)
>>> print process.communicate()[0].split(';')
(In your specific example you could use os.environ but I realise that's not really what you're asking.)
I updated my python interpreter, but I think the old one is still called. When I check for the version I get:
$ python -V
Python 3.0.1
But I believe the old interpreter is still being called. When I run the command:
python myProg.py
The script runs properly. But when I invoke it with the command
./myProg.py
I get the error message:
AttributeError: 'str' object has no attribute 'format'
Which apparently is due to the old interpreter being called. How can I fix this? I run Mac OS X 10.5. Has it something to do with the first line:
#!/usr/bin/python
I just started out with python and am not very familiar with interpreted languages, so I am not too sure what is going on.
According to the first line of the script, #!/usr/bin/python, you are calling the Python interpreter at /usr/bin/python (which is most likely the one that ships with Mac OS X). You have to change that path to the path where you installed your Python 3 interpreter (likely /usr/local/bin/python or /opt/local/bin/python); or you can just change that line to read #!/usr/bin/env python, which will call the python listed first in your PATH variable (which seems to be the newer version you installed).
Firstly, the recommended shebang line is:
#!/usr/bin/env python
This will make sure the python interpreter that is invoked when you ./foo.py is the same interpreter that is invoked when you invoke python from the command line.
From your description, I suspect that if you did:
which python
It would not give you /usr/bin/python. It would give you something else, which is where the python 3 interpreter lives. You can either modify your shebang line to the above, or replace the path to the python interpreter with the path returned by which.
Try which python. I will tell you which python interpreter is used in your environment.
If it is not /usr/bin/python like in the script, then your suspicion is confirmed.
It's very possibly what you suspect, that the shebang line is calling the older version. Two things you might want to check:
1) what version is the interpreter at /usr/bin/python:
/usr/bin/python -V
2) where is the python 3 interpreter you installed:
which python
If you get the correct one from the command line, then replace your shebang line with this:
#!/usr/bin/env python
Addendum: You could also replace the older version of python with a symlink to python 3, but beware that any major OS X updates (ie: 10.5.6 to 10.5.7) will likely break this:
sudo mv /usr/bin/python /usr/bin/python25
sudo ln -s /path/to/python/3/python /usr/bin/python
run 'which python' - if this gives a different answer than /usr/bin/python, change #!/usr/bin/python to have that path instead.
It may be a bit odd providing a Perl script to answer a Python question, but it works for Python just as well as it does for Perl. This is a script called 'fixin', meaning 'fix interpreter'. It changes the shebang line to the correct string for your current PATH.
#!/Users/jleffler/perl/v5.10.0/bin/perl
#
# #(#)$Id: fixin.pl,v 1.3 2003/03/11 21:20:08 jleffler Exp $
#
# FIXIN: from Programming Perl
# Usage: fixin [-s] [file ...]
# Configuration
$does_hashbang = 1; # Kernel recognises #!
$verbose = 1; # Verbose by default
# Construct list of directories to search.
#absdirs = reverse grep(m!^/!, split(/:/, $ENV{'PATH'}, 999));
# Process command line arguments
if ($ARGV[0] eq '-s')
{
shift;
$verbose = 0;
}
die "Usage: $0 [-s] [file ...]\n" unless #ARGV || !-t;
#ARGV = '-' unless #ARGV;
# Process each file.
FILE: foreach $filename (#ARGV)
{
open(IN, $filename) || ((warn "Can't process $filename: $!\n"), next);
$_ = <IN>;
next FILE unless /^#!/; # Not a hash/bang file
chop($cmd = $_);
$cmd =~ s/^#! *//;
($cmd, $arg) = split(' ', $cmd, 2);
$cmd =~ s!^.*/!!;
# Now look (in reverse) for interpreter in absolute path
$found = '';
foreach $dir (#absdirs)
{
if (-x "$dir/$cmd")
{
warn "Ignoring $found\n" if $verbose && $found;
$found = "$dir/$cmd";
}
}
# Figure out how to invoke interpreter on this machine
if ($found)
{
warn "Changing $filename to $found\n" if $verbose;
if ($does_hashbang)
{
$_ = "#!$found";
$_ .= ' ' . $arg if $arg ne '';
$_ .= "\n";
}
else
{
$_ = <<EOF;
:
eval 'exec $found $arg -S \$0 \${1+"\$#"}'
if \$running_under_some_shell;
EOF
}
}
else
{
warn "Can't find $cmd in PATH, $filename unchanged\n" if $verbose;
next FILE;
}
# Make new file if necessary
if ($filename eq '-') { select(STDOUT); }
else
{
rename($filename, "$filename.bak") ||
((warn "Can't modify $filename"), next FILE);
open(OUT, ">$filename") ||
die "Can't create new $filename: $!\n";
($def, $ino, $mode) = stat IN;
$mode = 0755 unless $dev;
chmod $mode, $filename;
select(OUT);
}
# Print the new #! line (or the equivalent) and copy the rest of the file.
print;
while (<IN>)
{
print;
}
close IN;
close OUT;
}
The code is derived from a script of the same name in the original Camel Book ('Programming Perl', first edition). This copy has been hacked a bit since then - and should be hacked some more. But I use it routinely -- indeed, I just copied it from one Mac to another, and since I've not installed Perl 5.10.0 on the second, I ran:
$ perl fixin fixin
Changing fixin to /usr/bin/perl
$
Thereby changing from the private install Perl to the standard one.
Exercise for the reader - rewrite the script in Python.