Why does the "c" character go missing in the following example?
var="\bINSERT"
echo abc${var}def
> abINSERTdef
Is there any documentation that tells me how to do similar things or disable the behaviour?
I can't find any shell variables documentation.
The command substitution isn't doing anything wrong -- it's echo whose behavior is surprising (legally; POSIX allows, but does not require, echo to interpret backslash escape sequences by default, so zsh is not in the wrong here).
printf '%s\n' abc${var}def
...doesn't have your problem (but for portability to non-zsh shells, I would quote "abc${var}def").
See Why is printf better than echo? on Unix & Linux Stack Exchange, and the POSIX spec for echo at https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
As already was mentioned in the comments, the zsh implementation of the internal echo differs from the one implemented by the external echo command.
If you want to continue using echo, there are several workarounds:
Do a echo -E ...; the -E turns off the backslash interpretation.
Do a command echo ...; the keyword command forces the shell to use the external echo command.
Set in the shell the option set bsd_echo and then just leave the echo command as it is. The option tells the shell to not use by default the backslash interpretation for its internal echo command.
Related
This is my script, in which I want to receive arguments --path and --mode by user, for example:
./myscript.sh --path=/hello --mode=a
script
#!/usr/bin/env bash
set -e
HELP=false
MODE=false
PATH=false
for arg in "$#"
do
case "$arg" in
-h|--help)
HELP=true
;;
--mode*)
MODE=`echo $arg | sed -e 's/^[^=]*=//g'`
;;
--path*)
PATH=`echo $arg | sed -e 's/^[^=]*=//g'`
;;
*)
echo "wrong argument: $arg"
echo "type --help for supported parameters"
exit 1
;;
esac
done
When I try to execute, I receive this error:
line 19: sed: command not found
What's wrong ?
PATH=false
This line destroys your PATH variable. PATH is where the shell looks up all commands you want to run. (External commands, that is; built-in shell commands, functions, and aliases are not affected.)
Normally it contains things like PATH=/usr/local/bin:/usr/bin:/bin. By setting it to false you're telling bash to search the (non-existent) directory false for commands.
This is why sed (which is an external command) cannot be found.
In general you should avoid ALL_UPPERCASE names for your own script variables because many of those ALL_UPPERCASE variables are reserved / already used by the shell or the system in general.
You said you're running this on Windows. I believe environment variables are case-insensitive on Windows, so even using path instead may not help here. You may have to choose a different name for your variable, such as my_path.
The nice answer #melpomene gave fixes your immediate sed problem, and it also mentions one good habit in shell programming -- avoiding uppercase names for non-environment variables (you might find Google's Shell Style Guide, variable names section helpful).
But besides that, your sed line is completely unnecessary. You can replace the expensive command substitution and call to the external sed:
MODE=`echo $arg | sed -e 's/^[^=]*=//g'
with shell parameter expansion, prefix pattern strip:
mode="${arg#*=}"
Also, when using command substitution, better use the $(command) form instead of backticks (also mentioned in the style guide cited above). It'll be easier to read, and to nest other calls to command substitutions (all characters between the parentheses make up the command; none are treated specially).
When iterating over arguments, you might find the while (($# > 0)) loop more convenient (coupled with shift N), if you need to handle parameters with varying number of arguments, e.g. -m val, -mval and --mode=val. But in that case you would be better off with getopts (few examples here) anyway.
I have
$>echo -n "\033[40m "
If I evoke it on command line, vs. save it to a file and evoke the file on command line, the result would be different.
I am using zsh on mac, but expect this to also work with bash. Shouldn't output be exactly the same? If not, why?
The behavior of echo -n is different between shells (and zsh and bash are indeed different shells) because it is, literally, undefined behavior: The POSIX sh specification does not require a shell to behave in any particular way when processing it:
The following operands shall be supported:
string - A string to be written to standard output. If the first operand is -n, or if any of the operands contain a backslash ( \ ) character, the results are implementation-defined.
When using XSI extensions, echo is required to process backslash-escape sequences like \033 with no further arguments given; without those extensions, it isn't required to process those sequences at all, but is purely implementation-defined behavior (thus dependent on your specific shell).
As a well-defined alternative, consider:
#!/bin/sh
# ^^ - because this code doesn't use any shell-specific extensions;
# use #!/bin/zsh if zsh-only, #!/bin/bash if bash-only, etc.
printf '%b' "\033[40m "
The above will behave identically, whether invoked with . yourscript, ./yourscript, bash yourscript, sh yourscript or zsh yourscript, because it's entirely well-defined by the POSIX specification (and doesn't rely on any of those behaviors for which zsh has chosen to break that specification).
If instead you were using extensions specific to a shell, it would need to be invoked either with ./yourscript (to honor the first line to select a shell according to its contents), with . yourscript from that same shell only, or with <shellname> yourscript, again, with the specific shell.
For this reason, the ./yourscript usage is strongly preferred, as it lets the script itself control its own interpreter.
Quoting again from the POSIX echo spec, this time from the APPLICATION USAGE section:
It is not possible to use echo portably across all POSIX systems unless both -n (as the first argument) and escape sequences are omitted.
The printf utility can be used portably to emulate any of the traditional behaviors of the echo utility as follows (assuming that IFS has its standard value or is unset):
The historic System V echo and the requirements on XSI implementations in this volume of IEEE Std 1003.1-2001 are equivalent to:
printf "%b\n" "$*"
The BSD echo is equivalent to:
if [ "X$1" = "X-n" ]
then
shift
printf "%s" "$*"
else
printf "%s\n" "$*"
fi
New applications are encouraged to use printf instead of echo.
I'm writing a bash script and when I write the line:
echo "l=l.split('\n')"
I would like the output to actually be l=l.split('\n') but get:
l=l.split('
')
Any idea on how to fix this? I tried using quotations at different spots and escaping the characters differently but nothing seems to be working. Appreciate the help!
**Worth noting - if I simply type the echo command into the terminal I get my desired output.. Not sure why a script is treated differently.
It sounds like perhaps you got the shebang wrong (for a bash script, anyway). Take for example:
$ cat test.sh
#!/bin/sh
echo "l=l.split('\n')"
$ ./test.sh
l=l.split('
')
$ cat test.bash
#!/bin/bash
echo "l=l.split('\n')"
$ ./test.bash
l=l.split('\n')
Even though bash and sh may be provided by the same shell on some systems, there are subtle differences in their behavior. If you want it to behave like it does for you in a terminal, be sure to use #!/bin/bash.
tl;dr
If your script is really being run by bash, then something must have turned on the off-by-default xpg_echo shell option - either directly, or indirectly via shell option posix.
posix is implicitly turned on when Bash is invoked as sh (/bin/sh), which happens on macOS, for instance.
If your script is not being run by bash, which is the likeliest explanation, as suggested in FatalError's helpful answer:
Ensure that your script's shebang line is either
#!/bin/bash or #!/usr/bin/env bash.
Alternatively, pass it directly to bash: bash <script>
The portable solution, which shields you from variable echo behavior, is to use printf as follows:
printf '%s\n' "l=l.split('\n')"
Optional background information
In bash, the interpretation of escape sequences such as \n by echo is turned OFF by default.
Option xpg_echo must be turned on for escape sequences to be recognized by echo.
This option is implicitly on if you run bash in POSIX compatibility mode (verify with shopt -o posix), which also happens if you run Bash as sh, as is the case on macOS, for instance, where /bin/sh is Bash.
Note that irrespective of the state of xpg_echo by itself, you can opt into escape-sequence interpretation ad-hoc with echo -e and opt out with echo -E.
However, this does not work when running in POSIX compatibility mode (shopt -o posix), where - in compliance with POSIX - echo supports no options at all.
In other words: The following would only work if (a) your script is really being executed by bash and (b) option posix is off:
echo -E "l=l.split('\n')"
Again, printf is the portable, POSIX-compliant alternative that works consistently across all POSIX-compatible shells, irrespective of the state of shell options:
# '%s\n': *without* escape-sequence interpretation
$ printf '%s\n' "l=l.split('\n')"
l=l.split('\n')
# '%b\n': *with* escape-sequence interpretation
$ printf '%b\n' "l=l.split('\n')"
l=l.split('
')
This solves the problem.
echo "l=l.split('\\\n')"
I've read the man pages on echo, and it tells me that the -e parameter will allow an escaped character, such as an escaped n for newline, to have its special meaning. When I type the command
$ echo -e 'foo\nbar'
into an interactive bash shell, I get the expected output:
foo
bar
But when I use this same command (i've tried this command character for character as a test case) I get the following output:
-e foo
bar
It's as if echo is interpretting the -e as a parameter (because the newline still shows up) yet also it interprets the -e as a string to echo. What's going on here? How can I prevent the -e showing up?
You need to use #!/bin/bash as the first line in your script. If you don't, or if you use #!/bin/sh, the script will be run by the Bourne shell and its echo doesn't recognize the -e option. In general, it is recommended that all new scripts use printf instead of echo if portability is important.
In Ubuntu, sh is provided by a symlink to /bin/dash.
Different implementations of echo behave in annoyingly different ways. Some don't take options (i.e. will simply echo -e as you describe) and automatically interpret escape sequences in their parameters. Some take flags, and don't interpret escapes unless given the -e flag. Some take flags, and interpret different escape sequences depending on whether the -e flag was passed. Some will cause you to tear your hair out if you try to get them to behave in a predictable manner... oh, wait, that's all of them.
What you're probably seeing here is a difference between the version of echo built into bash vs /bin/echo or maybe vs. some other shell's builtin. This bit me when Mac OS X v10.5 shipped with a bash builtin echo that echoed flags, unlike what all my scripts expected...
In any case, there's a solution: use printf instead. It always interprets escape sequences in its first argument (the format string). The problems are that it doesn't automatically add a newline (so you have to remember do that explicitly), and it also interprets % sequences in its first argument (it is, after all, a format string). Generally, you want to put all the formatting stuff in the format string, then put variable strings in the rest of the arguments so you can control how they're interpreted by which % format you use to interpolate them into the output. Some examples:
printf "foo\nbar\n" # this does what you're trying to do in the example
printf "%s\n" "$var" # behaves like 'echo "$var"', except escapes will never be interpreted
printf "%b\n" "$var" # behaves like 'echo "$var"', except escapes will always be interpreted
printf "%b\n" "foo\nbar" # also does your example
Use
alias echo /usr/bin/echo
to force 'echo' invoking coreutils' echo which interpret '-e' parameter.
Try this:
import subprocess
def bash_command(cmd):
subprocess.Popen(['/bin/bash', '-c', cmd])
code="abcde"
// you can use echo options such as -e
bash_command('echo -e "'+code+'"')
Source: http://www.saltycrane.com/blog/2011/04/how-use-bash-shell-python-subprocess-instead-binsh/
VAR="-e xyz"
echo $VAR
This prints xyz, for some reason. I don't seem to be able to find a way to get a string to start with -e.
What is going on here?
The answers that say to put $VAR in quotes are only correct by side effect. That is, when put in quotes, echo(1) receives a single argument of -e xyz, and since that is not a valid option string, echo just prints it out. It is a side effect as echo could just as easily print an error regarding malformed options. Most programs will do this, but it seems GNU echo (from coreutils) and the version built into bash simply echo strings that start with a hyphen but are not valid argument strings. This behaviour is not documented so it should not be relied upon.
Further, if $VAR contains a valid echo option argument, then quoting $VAR will not help:
$ VAR="-e"
$ echo "$VAR"
$
Most GNU programs take -- as an argument to mean no more option processing — all the arguments after -- are to be processed as non-option arguments. bash echo does not support this so you cannot use it. Even if it did, it would not be portable. echo has other portability issues (-n vs \c, no -e).
The correct and portable solution is to use printf(1).
printf "%s\n" "$VAR"
The variable VAR contains -e xyz, if you access the variable via $ the -e is interpreted as a command-line option for echo. Note that the content of $VAR is not wrapped into "" automatically.
Use echo "$VAR" to fix your problem.
In zsh, you can use a single dash (-) before your arguments. This ensures that no following arguments are interpreted as options.
% VAR="-e xyz"
% echo - $VAR
-e xyz
From the zsh docs:
echo [ -neE ] [ arg ... ]
...
Note that for standards compliance a double dash does not
terminate option processing; instead, it is printed directly.
However, a single dash does terminate option processing, so the
first dash, possibly following options, is not printed, but
everything following it is printed as an argument.
The single dash behaviour is different from other shells.
Keep in mind this behavior is specific to zsh.
Try:
echo "$VAR"
instead.
(-e is a valid option for echo - this is what causes this phenomenon).