I need to do a string manipuilation in shell script (/bin/dash):
#!/bin/sh
PORT="-p7777"
echo $PORT
echo ${PORT/p/P}
the last echo fails with Bad substitution. When I change shell to bash, it works:
#!/bin/bash
PORT="-p7777"
echo $PORT
echo ${PORT/p/P}
How can I implement the string substitution in dash ?
The substitution you're using is not a basic POSIX feature (see here, in section 2.6.2 Parameter Expansion), and dash doesn't implement it.
But you can do it with any of a number of external helpers; here's an example using sed:
PORT="-p7777"
CAPITOLPORT=$(printf '%s\n' "$PORT" | sed 's/p/P/')
printf '%s\n' "$CAPITOLPORT"
BTW, note that I'm using printf '%s\n' instead of echo -- that's because some implementations of echo do unpredictable things when their first argument starts with "-". printf is a little more complicated to use (you need a format string, in this case %s\n) but much more reliable. I'm also double-quoting all variable references ("$PORT" instead of just $PORT), to prevent unexpected parsing.
I'd also recommend switching to lower- or mixed-case variables. There are a large number of all-caps variable that have special meanings, and if you accidentally use one of those it can cause problems.
Using parameter expansion:
$ cat foo.sh
#!/bin/sh
PORT="-p7777"
echo $PORT
echo ${PORT:+-P${PORT#-p}}
PORT=""
echo $PORT
echo ${PORT:+-P${PORT#-p}}
Run it:
$ /bin/sh foo.sh
-p7777
-P7777
Update:
$ man dash:
- -
${parameter#word} Remove Smallest Prefix Pattern.
$ echo ${PORT#-p}
7777
$ man dash
- -
${parameter:+word} Use Alternative Value.
$ echo ${PORT:+-P${PORT#-p}}
-P7777
How do I print a newline? This merely prints \n:
$ echo -e "Hello,\nWorld!"
Hello,\nWorld!
Use printf instead:
printf "hello\nworld\n"
printf behaves more consistently across different environments than echo.
Make sure you are in Bash.
$ echo $0
bash
All these four ways work for me:
echo -e "Hello\nworld"
echo -e 'Hello\nworld'
echo Hello$'\n'world
echo Hello ; echo world
echo $'hello\nworld'
prints
hello
world
$'' strings use ANSI C Quoting:
Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.
You could always do echo "".
For example,
echo "Hello,"
echo ""
echo "World!"
On the off chance that someone finds themselves beating their head against the wall trying to figure out why a coworker's script won't print newlines, look out for this:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
echo $(GET_RECORDS);
As in the above, the actual running of the method may itself be wrapped in an echo which supersedes any echos that may be in the method itself. Obviously, I watered this down for brevity. It was not so easy to spot!
You can then inform your comrades that a better way to execute functions would be like so:
#!/bin/bash
function GET_RECORDS()
{
echo -e "starting\n the process";
}
GET_RECORDS;
Simply type
echo
to get a new line
POSIX 7 on echo
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html
-e is not defined and backslashes are implementation defined:
If the first operand is -n, or if any of the operands contain a <backslash> character, the results are implementation-defined.
unless you have an optional XSI extension.
So I recommend that you should use printf instead, which is well specified:
format operand shall be used as the format string described in XBD File Format Notation [...]
the File Format Notation:
\n <newline> Move the printing position to the start of the next line.
Also keep in mind that Ubuntu 15.10 and most distros implement echo both as:
a Bash built-in: help echo
a standalone executable: which echo
which can lead to some confusion.
str='hello\nworld'
$ echo | sed "i$str"
hello
world
You can also do:
echo "hello
world"
This works both inside a script and from the command line.
On the command line, press Shift+Enter to do the line break inside the string.
This works for me on my macOS and my Ubuntu 18.04 (Bionic Beaver) system.
For only the question asked (not special characters etc) changing only double quotes to single quotes.
echo -e 'Hello,\nWorld!'
Results in:
Hello,
World!
There is a new parameter expansion added in Bash 4.4 that interprets escape sequences:
${parameter#operator} - E operator
The expansion is a string that is the value of parameter with
backslash escape sequences expanded as with the $'…' quoting
mechanism.
$ foo='hello\nworld'
$ echo "${foo#E}"
hello
world
I just use echo without any arguments:
echo "Hello"
echo
echo "World"
To print a new line with echo, use:
echo
or
echo -e '\n'
This could better be done as
x="\n"
echo -ne $x
-e option will interpret backslahes for the escape sequence
-n option will remove the trailing newline in the output
PS: the command echo has an effect of always including a trailing newline in the output so -n is required to turn that thing off (and make it less confusing)
My script:
echo "WARNINGS: $warningsFound WARNINGS FOUND:\n$warningStrings
Output:
WARNING : 2 WARNINGS FOUND:\nWarning, found the following local orphaned signature file:
On my Bash script I was getting mad as you until I've just tried:
echo "WARNING : $warningsFound WARNINGS FOUND:
$warningStrings"
Just hit Enter where you want to insert that jump. The output now is:
WARNING : 2 WARNINGS FOUND:
Warning, found the following local orphaned signature file:
If you're writing scripts and will be echoing newlines as part of other messages several times, a nice cross-platform solution is to put a literal newline in a variable like so:
newline='
'
echo "first line${newline}second line"
echo "Error: example error message n${newline}${usage}" >&2 #requires usage to be defined
If the previous answers don't work, and there is a need to get a return value from their function:
function foo()
{
local v="Dimi";
local s="";
.....
s+="Some message here $v $1\n"
.....
echo $s
}
r=$(foo "my message");
echo -e $r;
Only this trick worked on a Linux system I was working on with this Bash version:
GNU bash, version 2.2.25(1)-release (x86_64-redhat-linux-gnu)
You could also use echo with braces,
$ (echo hello; echo world)
hello
world
This got me there....
outstuff=RESOURCE_GROUP=[$RESOURCE_GROUP]\\nAKS_CLUSTER_NAME=[$AKS_CLUSTER_NAME]\\nREGION_NAME=[$REGION_NAME]\\nVERSION=[$VERSION]\\nSUBNET-ID=[$SUBNET_ID]
printf $outstuff
Yields:
RESOURCE_GROUP=[akswork-rg]
AKS_CLUSTER_NAME=[aksworkshop-804]
REGION_NAME=[eastus]
VERSION=[1.16.7]
SUBNET-ID=[/subscriptions/{subidhere}/resourceGroups/makeakswork-rg/providers/Microsoft.Network/virtualNetworks/aks-vnet/subnets/aks-subnet]
Sometimes you can pass multiple strings separated by a space and it will be interpreted as \n.
For example when using a shell script for multi-line notifcations:
#!/bin/bash
notify-send 'notification success' 'another line' 'time now '`date +"%s"`
With jq:
$ jq -nr '"Hello,\nWorld"'
Hello,
World
Additional solution:
In cases, you have to echo a multiline of the long contents (such as code/ configurations)
For example:
A Bash script to generate codes/ configurations
echo -e,
printf might have some limitation
You can use some special char as a placeholder as a line break (such as ~) and replace it after the file was created using tr:
echo ${content} | tr '~' '\n' > $targetFile
It needs to invoke another program (tr) which should be fine, IMO.
I'm trying to configure a file with a bash script. And the variables in the bash script are not written in file as it is written in script.
Ex:
#!/bin/bash
printf "%s" "template("$DATE\t$HOST\t$PRIORITY\t$MSG\n")" >> /file.txt
exit 0
This results to template('tttn') instead of template("$DATE\t$HOST\t$PRIORITY\t$MSG\n in file.
How do I write in the script so that the result is template("$DATE\t$HOST\t$PRIORITY\t$MSG\n in the configured file?
Is it possible to write variable as it looks in script to file?
Enclose the strings you want to write within single quotes to avoid variable replacement.
> FOO=bar
> echo "$FOO"
bar
> echo '$FOO'
$FOO
>
Using printf in any shell script is uncommon, just use echo with the -e option.
It allows you to use ANSI C metacharacters, like \t or \n. The \n at the end however isn't necessary, as echo will add one itself.
echo -e "template(${DATE}\t${HOST}\t${PRIORITY}\t${MSG})" >> file.txt
The problem with what you've written is, that ANSI C metacharacters, like \t can only be used in the first parameter to printf.
So it would have to be something like:
printf 'template(%s\t%s\t%s\t%s)\n' ${DATE} ${HOST} ${PRIORITY} ${MSG} >> file.txt
But I hope we both agree, that this is very hard on the eyes.
There are several escaping issues and the power of printf has not been used, try
printf 'template(%s\t%s\t%s\t%s)\n' "${DATE}" "${HOST}" "${PRIORITY}" "${MSG}" >> file.txt
Reasons for this separate answer:
The accepted answer does not fit the title of the question (see comment).
The post with the right answer
contains wrong claims about echo vs printf as of this post and
is not robust against whitespace in the values.
The edit queue is full at the moment.
This question already has an answer here:
Bash: Confused by expanding asterisk
(1 answer)
Closed 7 years ago.
My problem can be reproduced with the following script:
#!/bin/bash
echo ".*" > foo.txt
echo $(cat foo.txt)
When I run this script I get a list of folders/files in my current directory:
$ ./test.sh
. .. .testfile
$ cat foo.txt
.*
My question is really a two parter:
1) Why does this happen?
2) Is there any way to get the literal string ".*" rather than a file list returned from $(cat [args])?
I originally ran into this problem working on a more complex script. Fixing this with an additional option passed into cat and/or alternative syntax would be ideal.
echo $(cat foo.txt)
Capturing cat's output and then echoing it right back out is not only redundant, it's error prone, as you've discovered. Just write:
cat foo.txt
Simpler, faster, it's the bees knees!
Or if you really, really want to capture it and then print it back out, use quotes. Quotes will prevent the .* from being interpreted as wildcards.
echo "$(cat foo.txt)"
There are still subtle problems with this command. If foo.txt contains -n, for instance, echo won't print -n, it'll print nothing. It turns out that echo simply isn't usable if you're the extra paranoid type. The super safe option is to eschew echo in favor of printf.
printf '%s\n' "$(cat foo.txt)"
This is as safe as one can get. It prints the contents of foo.txt and won't get tripped up by any special characters.
Although, you know, this is an awfully long winded way of writing:
cat foo.txt
Because the result returned by $() is un-quoted. It is equivalent to:
echo .*
which is subject to shell expansion.
You should double quote it:
echo "$(cat foo.txt)"
It will give correct output .*
Use More Quotes !
echo ".*" > foo.txt
echo "$(cat foo.txt)"
.*
The double quotes are mandatory, to avoid shell expansion:
Without the double quotes, all the characters are expanded from the shell, so the * becomes files in current directory
http://www.grymoire.com/Unix/Quote.html shows a list of special characters. Is there a parameter/option for echo where I can treat everything that comes after the echo as a string?
In python, i could use the """...""" or '''...'''.
$ python
>>> text = '''#"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&'''
>>> print text
#"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&
I can do the same in unix's echo with ''' but not """, why is that so?
$ echo #"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&
$ echo '''#"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&'''
#"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&
$ echo """'''#"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&"""
bash: !###%$#$^%$: event not found
What happens if i have a string like this?
#"`\|^!###%$#$^%'''$&%^*()?/\;:$#$"""#$?$$$!&&
How should I echo such a string? (the following command doesn't work)
echo '''#"`\|^!###%$#$^%'''$&%^*()?/\;:$#$"""#$?$$$!&&'''
Use printf:
$ printf "%s\n" $'#"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&'
#"`\|^!###%$#$^%$&%^*()?/\;:$#$#$?$$$!&&
$ printf "%s\n" $'#"`\|^!###%$#$^%\'\'\'$&%^*()?/\;:$#$"""#$?$$$!&&'
#"`\|^!###%$#$^%'''$&%^*()?/\;:$#$"""#$?$$$!&&
You might note that single quotes ' need to be escaped.
In order to assign the output to a variable:
$ foo=$(printf "%s\n" $'#"`\|^!###%$#$^%\'\'\'$&%^*()?/\;:$#$"""#$?$$$!&&')
$ echo $foo
#"`\|^!###%$#$^%'''$&%^*()?/\;:$#$"""#$?$$$!&&
It is because shell applies all expansion rules inside a string double quotes ". $ or ! are special Unix characters to denote variable or event hence you get that error.
I think it is because of your shell (bash), which expands/interprets double quotes.
This does not apply for single quotes.
For details, please have a look at Bash - Shell Expansion.
For the echo command there is the -e option which enables interpretation of backslash escapes - which might help.