Pass output of a bash script as command line argument for another script - bash

Beginner in bash and makefiles here. I have a course where we need to create a makefile where each rule calls one of the already compiled programs. All of the compiled programs take a command line argument. As the arguments can be quite large and mostly consists of the same character in a row (for example AAAAAAA) I made a script that uses python to print the argument. Example:
#!/bin/bash
python -c 'print "A"*1000 + "Q"*200'
I am wondering how to create the rule in the makefile so that the output of the above script will be passed as the command line argument. Essentially like this:
test:
./schoolprogram ./myprogram.sh
So when make test is executed then ./schoolprogram should be run with the argument 1000 A's followed by 200 Q's and not the literal string "./myprogram.sh".

I don't know why you have a script that does nothing but invoke python; why not just run python directly?
In any event, this isn't really a makefile question it's mostly a shell question. When writing makefile recipes, the best way is to get the command you want to run working at your shell prompt, then take that same command (with one small tweak) and put it into your makefile.
In the shell, you can use either $(...) or backticks (an older style) to run a command and replace it with the output of that command. So you can run this:
$ ./schoolprogram $(./myprogram.sh)
or more easily:
$ ./schoolprogram $(python -c 'print "A"*1000 + "Q"*200')
Now when you take a command and put it into a makefile recipe, the thing you have to remember is that the $ character is special to make, so if you want a $ to be passed to your command you have to escape it by doubling it, $$. So you'd write:
test:
./schoolprogram $$(./myprogram.sh)
or equivalently:
test:
./schoolprogram $$(python -c 'print "A"*1000 + "Q"*200')

Related

Guile scheme system* with pdflatex -jobname

I'm trying to call pdflatex from a guile scheme file. This is the Guile command I'm using:
(system*
"cat" "foo.txt" "|" "pdflatex" "-jobname" "\"bar\"")
This is the error I get back after running the file:
cat: invalid option -- 'j'
Try 'cat --help' for more information.
If I run the command from bash shell it runs normally.
cat foo.txt | pdflatex -jobname "bar"
-jobname is the correct command for pdflatex, but system* seems to have a problem with it.
I'm using (GNU Guile) 2.2.4 and pdfTeX 3.14159265-2.6-1.40.20.
Use system, not system*. It takes a single string as the argument, and executes it using the shell, which will perform the desired piping.
(system "cat foo.txt | pdflatex -jobname 'bar'")
system* doesn't use the shell. As the manual explains:
system* is similar to system, but accepts only one string per-argument, and performs no shell interpretation. The command is executed using fork and execlp. Accordingly this function may be safer than system in situations where shell interpretation is not required.
Note that your command is a Useless use of cat since pdflatex takes the filename as an argument. You could use system* to execute it directly.
(system* "pdflatex" "-jobname" "bar" "foo.txt")
Also, you don't need to add extra quotes around bar when you use system*; since it doesn't use the shell, it doesn't parse special characters.

GNU Make and using ; to execute multiple shell commands in the same command shell

I have a very basic problem using GNU Make 3.81 on Windows, I must be doing something very silly and I'm sure someone here will point it out in milliseconds. My problem is with using ";" to run multiple commands in the same shell.
As I understand it, make runs each line in its own command shell and so if you want to run two commands, one after the other, you must put them on the same line separated by a semicolon. In it's simplest form:
all:
echo hello; echo hello
...should produce the output:
hello
hello
But for me it produces the output:
hello; echo hello
In other words, the semicolon is being passed straight through to the shell, which doesn't make too much sense for cmd.exe.
I'm now ready to be embarrassed by everyone pointing out where I've gone wrong...
FYI, the reason I need this is that I'm using a $(foreach) loop which must execute two shell commands for each iteration.
You are be under the impression that ; is a GNU-make operator for executing multiple
commands in the same shell within a recipe. Not so. It is linux shell operator
for punctuating a sequence of commands on the same line. It is not an operator for
the Windows shell, cmd, so when the recipe:
echo hello; echo hello
is executed by make on Linux, it has the output you expect, but when executed by make
on Windows it just means echo this:
hello; echo hello
So, the answer is that your shell is the thing that has to understand that ; separates multiple commands on the same line, it's nothing to do with make. This is not the case for Windows cmd.exe but is presumably the case for the shells that normally arrive with environments that use make (Linux, msys etc.). In my case, a good workaround was this:
define useDef
echo hello
echo hello
endef
all:
$(call useDef)
With this form of "single-lined" definition I can invoke a multiline shell command inside $(foreach). Make still does each "hello" in its own shell but in my case that's OK because I'm appending outputs to a file. If you need two commands to be run in the same shell for some reason then, on Windows, you would need to write a separate batch file (which I suppose you could create from inside the makefile).
I know this question is relatively old, but I've stumbled across the same problem recently. The solution (for me) was quite simple. I replaced ; with &.
Basically
all:
echo hello & echo hello
will produce
hello
hello
in cmd.exe.
And it works with $(foreach) loops as well.
UPD: You also can use && instead of & if you don't want your commands to fail silently.

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.

how does this escaping work?

Here is what it finally took to get my code in my makefile to work
Line 5 is the question area
BASE=50
INCREMENT=1
FORMATTED_NUMBER=${BASE}+${INCREMENT}
all:
echo $$((${FORMATTED_NUMBER}))
why do i have to add two $ and two (( )) ?
Formatted_Number if i echo it looks like "50+1" . What is the logic that make is following to know that seeing $$(("50+1")) is actually 51?
sorry if this is a basic question i'm new to make and dont fully understand it.
First, whenever asking questions please provide a complete example. You're missing the target and prerequisite here so this is not a valid makefile, and depending on where they are it could mean very different things. I'm assuming that your makefile is something like this:
BASE=50
INCREMENT=1
FORMATTED_NUMBER=${BASE}+${INCREMENT}
all:
echo $$((${FORMATTED_NUMBER}))
Makefiles are interesting in that they're a combination of two different formats. The main format is makefile format (the first five lines above), but inside a make recipe line (that's the last line above, which is indented with a TAB character) is shell script format.
Make doesn't know anything about math. It doesn't interpret the + in the FORMATTED_NUMBER value. Make variables are all strings. If you want to do math, you have to do it in the shell, in a shell script, using the shell's math facilities.
In bash and other modern shells, the syntax $(( ...expression... )) will perform math. So in the shell if you type echo $((50+1)) (go ahead and try it yourself) it will print 51.
That's why you need the double parentheses ((...)): because that's what the shell wants and you're writing a shell script.
So why the double $? Because before make starts the shell to run your recipe, it first replaces all make variable references with their values. That's why the shell sees 50+1 here: before make started the shell it expanded ${FORMATTED_NUMBER} into its value, which is ${BASE}+${INCREMENT}, then it expanded those variables so it ends up with 50+1.
But what if you actually want to use a $ in your shell script (as you do here)? Then you have to tell make to not treat the $ as introducing a make variable. You do this by doubling it, so if make sees $$ then it does not think that's a make variable, and sends a single $ to the shell.
So for the recipe line echo $$((${FORMATTED_NUMBER})) make actually invokes a shell script echo $((50+1)).
You can use this in BASH:
FORMATTED_NUMBER=$((BASE+INCREMENT))
Is using non BASH use:
FORMATTED_NUMBER=`echo "$BASE + $INCREMENT" | bc`

Effects of comment (#) lines before and/or after the comment-like #!/bin/sh line

Example one
#!/bin/sh
# purpose: print out current directory name and contents
pwd
ls
Example two
# purpose: print out current directory name and contents
#!/bin/sh
pwd
ls
What is the difference – if I make the first line a comment(#), with #!/bin/sh as the second line, what will happen?
What is meaning of #!/bin/sh ?
Normally a shell script is run by your default shell defined in the /etc/passwd file. But you can define explicitly a program which can run your script.
Unices uses a common method to determine what program needed to run a specific script (man execve(2)). If the script has the proper execute rights set and in a script the first line starts with a #! characters, it will run by the program defined afterwards.
For example if the first line is #!/usr/bin/awk -f then the rest of the file will be passed to the awk program (so it has to use awk syntax). Or if a Makefile starts with #!/usr/bin/make -f then the rest of the file will be passed to make. You can start the script as a normal program and the script can be written in awk or make (or whatever defined) syntax.
If execve does not find #! as the first two character of the file, it will consider as a normal script file and it will run as it is.
So using #! You can determine the script language and You do not need to know what shell is used by the other user using your script. In any other line #! will be interpretered your default shell, which is usually just a comment line.
what is difference between 1st & 2nd shell scripts..?
No difference in output. But the time to execute both will be little different as the interpreter reads line one by one.
if i give comment(#) in 1st line after #!/bin/sh in 2nd line so what will happen ?
Any line started with (#) except the shebang(#!) is treated as a comment in shell script.
what is meaning of #!/bin/sh ?
Its the path(here - /bin/sh) to the interpreter used after the shebang (#!) . Shell will try to use the interpreter language mentioned after the shebang to execute the script.

Resources