how to escape `&>` in Makefile? - makefile

I use a :
python notation.py &> notation.log
Command line.
I try to run it from a Makefile with no success:
$ cat Makefile
run:
python notation.py &> notation.log
$ make
$ ls -la notation.log
-rw-rw-r-- 1 me me 0 juin 8 08:15 notation.log
$

The problem is not escaping. The problem is that is not legal syntax in the shell.
Make always invokes (by default) /bin/sh as the shell to run its recipes. /bin/sh is a POSIX-standard shell. The token &> is not valid POSIX shell syntax. When you are logged in at a shell prompt you are not running the shell /bin/sh, you are running a more powerful shell, probably bash (on Linux) or possibly zsh (on newer MacOS systems). These shells have extra features that are not defined in POSIX and not available in a POSIX-compliant version of /bin/sh.
You have two choices: either use correct POSIX syntax in your recipe:
run:
python notation.py > notation.log 2>&1
Or else tell make to use your shell when it runs recipes:
SHELL := /bin/bash
run:
python notation.py &> notation.log
(of course this assumes that all systems you want to run your makefile in, actually have /bin/bash installed)

Related

tcsh: How to find out if shell was started with -c flag?

tcsh, as with other shells, accepts the -c flag to execute a set of commands from the command-line args (instead of from a script) upon running the shell, such as:
tcsh -c 'mkdir /tmp/some-dir; tar -C /tmp/some-dir -xvf a-tarball.tar'
Is there a way to query the interpreter's state to detect that -c flag? Remember that this flag is passed on to tcsh, NOT to the commands that were fed to the interpreter via -c.
Background: I found a few days ago that tcsh -c "COMMANDS..." still invokes additional rc files (in particular, .cshrc) upon starting up. I have some commands in .cshrc that I do NOT want run when tcsh -c is invoked (as opposed to interactive tcsh).

Different environments in different shells

Suppose for the sake of simplicity that I am working with bash and zsh. In bash, I have a .bash_profile that puts ~/bash/bin in the PATH, and in zsh, I have in my .zshrc the path ~/zsh/bin in the PATH. Now, suppose I have two executables at ~/bash/bin/foobar and ~/zsh/bin/foobar. As such, if I run command -v foobar, I should be returning one of the two, depending if I was working in bash or zsh. The question I have is as follows: is it possible to, in a bash script, determine what command -v foobar would output in zsh, or vice-versa?
I'm not confident that
#!/bin/bash
zsh -c 'command -v foobar'
would give me the output of ~/zsh/bin/foobar in this case.
The command
zsh -c 'command -v foobar'
does not process ~/.zshrc , so you can't expect any effect of this file on this command. You could do a
zsh -i -c 'command -v foobar'
to force .zshrc to be processed, but this does not necessarily mean that you would see here a different directory. For instance, assume that in your bash, the PATH is set to /usr/bin:$HOME/bash/bin, and your .zshrc sets the PATH by doing a
PATH=$PATH:$HOME/zsh/bin
In this case, even zsh -i -c .... would still show $HOME/bash/bin/foobar as a match.

Which shell does Perl 6's shell() use?

Perl 6's shell sends commands to the "shell" but doesn't say what that is. I consistently get bash on my machine but I don't know if I can rely on that.
$ perl6 -e 'shell( Q/echo $SHELL/ )'
/bin/bash
$ csh
% perl6 -e 'shell( Q/echo $SHELL/ )'
/bin/bash
% zsh
$ perl6 -e 'shell( Q/echo $SHELL/ )'
/bin/bash
That's easy enough on Unix when it's documented, but what about cmd.exe or PowerShell on Windows (or bash if it's installed)? I figure it's the cmd.exe but a documented answer would be nice.
Looking at the source, rakudo just calls /bin/sh -c on non-windows and uses %*ENV<ComSpec> /c on windows.
dash (installed as /bin/sh on many systems), doesn't set $SHELL, nor should it. $SHELL isn't the name of the parent process; it's the name of the shell that should be used when an interactive shell is desired.
To get the name of the parent process, you could use the following on some systems:
echo "$0"
or
# Command line
perl -e'$ppid=getppid(); #ARGV="/proc/$ppid/cmdline"; CORE::say "".<>'
or
# Program file
perl -e'$ppid=getppid(); CORE::say readlink("/proc/$ppid/exe")'
You'll find you'll get /bin/sh in all cases.

Difference in bash printf output between run script and source script

I can't seem to find the difference between a script run two different ways.
Here's the script (named test.sh):
#! /bin/bash
printf "%b\n" "\u5A"
When the script is sourced:
. test.sh
> Z ## Result I want ##
When the script is run:
./test.sh
> \u5A ## Result I get ##
I want the run script to give the results of the sourced script... what setting do I need to set/change?
You are probably getting different versions of printf; the script you are sourcing from is probably a /bin/sh script, not a Bash script proper?
Shouldn't you be using \x instead of \u? printf "%b\n" "\x5A" works fine in both cases for me.
(Totally different idea here, so I'm posting it as another answer.)
Try running these at the command line:
builtin printf "%b\n" "\u5A"
/usr/bin/env printf "%b\n" "\u5A"
printf is both a shell builtin and an executable, and you may be getting different ones depending on whether you source or run the script. To find out, insert this in the script and run it each way:
type printf
While you're at it, you may as well insert this line too:
echo $SHELL
That will reveal if you're getting different shells, per tripleee.
HAHA!!! I finally traced down the problem! Read ahead if interested (leave the page if not).
These are the only command that will translate \u properly:
. ./test.sh ## Sourcing the script, hash-bang = #! /bin/sh
. ./test.bash ## Sourcing the script, hash-bang = #! /bin/bash
./test ## Running the script with no hash-bang
All of the following produce identical results in that they do NOT translate \u:
./test.sh ## Script is run from an interactive shell but in a non-interactive shell
## test.sh has first line: #! /bin/sh
/bin/sh -c "./test.sh" ## Running the script in a non-interactive sh shell
/bin/sh -lc "./test.sh" ## Running the script in a non-interactive, login sh shell
/bin/sh -c ". ./test.sh" ## Sourcing the file in a non-interactive sh shell
/bin/sh -lc ". ./test.sh" ## Sourcing the file in a non-interactive, login sh shell
## test.bash has first line: #! /bin/bash
/bin/bash -c "./test.bash" ## Running the script in a non-interactive bash shell
/bin/bash -lc "./test.bash" ## Running the script in a non-interactive, login bash shell
/bin/bash -c ". ./test.bash" ## Sourcing the file in a non-interactive bash shell
/bin/bash -lc ". ./test.bash" ## Sourcing the file in a non-interactive, login bash shell
## And from ***tripleee*** (thanks btw):
/bin/sh --norc; . ./test.sh ## Sourcing from an interactive sh shell without the ~/.bashrc file read
/bin/bash --norc; . ./test.bash ## Sourcing from an interactive bash shell without the ~/.bashrc file read
The only way to get proper translation is to run the script without a hash-bang... and I finally figured out why! Without a hash-bang my system chooses the default shell, which btw is NOT /bin/bash... it turns out to be /opt/local/bin/bash... two different versions of bash!
Finally, I removed the OSX /bin/bash [v3.2.48(1)] and replaced it with the MacPorts /opt/local/bin/bash [v4.2.10(2)] and now running the script works! It actually solved about 10-15 other problems I've had (like ${var,,}, read sN1 char, complete -EC "echo ' '", and a host of other commands I have scattered throughout my scripts, ~/.bashrc amd ~/.profile). Honestly, I really should have noticed when my scripts using associative arrays suddenly crapped out on me... how stupid can I get!?
I've been using bash v4 for a looong time now, and my Lion upgrade went and down-graded bash back to v3 (get with the program Apple!)... ugh, I feel so ashamed! Everyone still using bash v3, upgrade!! bash v4 is has many, many beautiful upgrades over version 3. Type bash --version to see what version you are running. One advantage is now bash can translate \uHEX into Unicode!
Try removing the space in the first line, I seem to recall that can cause problems. Offhand I'd guess that because of that space, you're not getting bash, but sh.
Glad you solved it. Still, you might be looking for a portable solution.
Assuming you are always using the same formatting string, we can just discard it, and use something like this;
printf () {
# Discard format string
shift
perl -CSD -le '
print map { s/^\\u//; chr(hex($_)) } #ARGV' "$#"
}
Edit to add: You would simply add this function definition at the beginning of your existing script, overriding the builtin printf. Obviously, if you also use printf for other stuff, this special-purpose replacement isn't good enough.
You could rename the function to uprintf or something, still. It merely translates a sequence of hex codes to the corresponding Unicode characters, discarding any \u prefix.

Bash: Syntax error: redirection unexpected

I do this in a script:
read direc <<< $(basename `pwd`)
and I get:
Syntax error: redirection unexpected
in an ubuntu machine
/bin/bash --version
GNU bash, version 4.0.33(1)-release (x86_64-pc-linux-gnu)
while I do not get this error in another suse machine:
/bin/bash --version
GNU bash, version 3.2.39(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
Why the error?
Does your script reference /bin/bash or /bin/sh in its hash bang line? The default system shell in Ubuntu is dash, not bash, so if you have #!/bin/sh then your script will be using a different shell than you expect. Dash does not have the <<< redirection operator.
Make sure the shebang line is:
#!/bin/bash
or
#!/usr/bin/env bash
And run the script with:
$ ./script.sh
Do not run it with an explicit sh as that will ignore the shebang:
$ sh ./script.sh # Don't do this!
If you're using the following to run your script:
sudo sh ./script.sh
Then you'll want to use the following instead:
sudo bash ./script.sh
The reason for this is that Bash is not the default shell for Ubuntu. So, if you use "sh" then it will just use the default shell; which is actually Dash. This will happen regardless if you have #!/bin/bash at the top of your script. As a result, you will need to explicitly specify to use bash as shown above, and your script should run at expected.
Dash doesn't support redirects the same as Bash.
Docker:
I was getting this problem from my Dockerfile as I had:
RUN bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
However, according to this issue, it was solved:
The exec form makes it possible to avoid shell string munging, and
to RUN commands using a base image that does not contain /bin/sh.
Note
To use a different shell, other than /bin/sh, use the exec form
passing in the desired shell. For example,
RUN ["/bin/bash", "-c", "echo hello"]
Solution:
RUN ["/bin/bash", "-c", "bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)"]
Notice the quotes around each parameter.
You can get the output of that command and put it in a variable. then use heredoc. for example:
nc -l -p 80 <<< "tested like a charm";
can be written like:
nc -l -p 80 <<EOF
tested like a charm
EOF
and like this (this is what you want):
text="tested like a charm"
nc -l -p 80 <<EOF
$text
EOF
Practical example in busybox under docker container:
kasra#ubuntu:~$ docker run --rm -it busybox
/ # nc -l -p 80 <<< "tested like a charm";
sh: syntax error: unexpected redirection
/ # nc -l -p 80 <<EOL
> tested like a charm
> EOL
^Cpunt! => socket listening, no errors. ^Cpunt! is result of CTRL+C signal.
/ # text="tested like a charm"
/ # nc -l -p 80 <<EOF
> $text
> EOF
^Cpunt!
do it the simpler way,
direc=$(basename `pwd`)
Or use the shell
$ direc=${PWD##*/}
Another reason to the error may be if you are running a cron job that updates a subversion working copy and then has attempted to run a versioned script that was in a conflicted state after the update...
On my machine, if I run a script directly, the default is bash.
If I run it with sudo, the default is sh.
That’s why I was hitting this problem when I used sudo.
In my case error is because i have put ">>" twice
mongodump --db=$DB_NAME --collection=$col --out=$BACKUP_LOCATION/$DB_NAME-$BACKUP_DATE >> >> $LOG_PATH
i just correct it as
mongodump --db=$DB_NAME --collection=$col --out=$BACKUP_LOCATION/$DB_NAME-$BACKUP_DATE >> $LOG_PATH
Before running the script, you should check first line of the shell script for the interpreter.
Eg:
if scripts starts with /bin/bash , run the script using the below command
"bash script_name.sh"
if script starts with /bin/sh, run the script using the below command
"sh script_name.sh"
./sample.sh - This will detect the interpreter from the first line of the script and run.
Different Linux distributions having different shells as default.

Resources