#!/usr/local/bin/bash
out=`grep apache README`
echo $out;
Usually grep shows each match on a separate line when run on the command line. However, in the above scripts, the newline separating each match disappears. Does anyone know how the newline can be preserved?
You're not losing it in the assignment but in the echo. You can see this clearly if you:
echo "${out}"
You'll see a similar effect with the following script:
x="Hello,
I
am
a
string
with
newlines"
echo "====="
echo ${x}
echo "====="
echo "${x}"
echo "====="
which outputs:
=====
Hello, I am a string with newlines
=====
Hello,
I
am
a
string
with
newlines
=====
And, irrelevant to your question but I'd like to mention it anyway, I prefer to use the $() construct rather than backticks, just for the added benefit of being able to nest commands. So your script line becomes:
out=$(grep apache README)
Now that may not look any different (and it isn't) but it makes possible more complex commands like:
lines_with_nine=$(grep $(expr 7 + 2) inputfile)
Put $out in quotes:
#!/usr/local/bin/bash
out=`grep apache README`
echo "$out";
Quoting variables in bash preserves the whitespace.
For instance:
#!/bin/bash
var1="A B C D"
echo $var1 # A B C D
echo "$var1" # A B C D
since newlines are whitespace they get "removed"
Combining other answers into a one liner:
echo "($(grep apache README))"
Related
Sometimes I want to create lots of whitespace at once (slightly different than a specific character). I attempted to do this using a for loop, but I am only printing \n once with this implementation. Furthermore, the actual '\n' character is actually printed instead of a blank line. What is a better way to do this?
for i in {1....100}
> do
> echo "\n"
> done
To print 5 blank lines:
yes '' | sed 5q
To print N blank lines:
yes '' | sed ${N}q
Brace expansion expects two dots, not any other number:
$ echo {1....5}
{1....5}
$ echo {1..5}
1 2 3 4 5
That's why your loop was executed just once.
If you want echo to interpret your escape sequences, you need to call it with echo -e.
echo outputs a newline anyway, so echo -e "\n" prints two newlines. To prevent echo from printing a newline you have to use echo -n, and echo -ne "\n" is the same as just echo.
You can print repeating characters, in this case a newline, like this:
printf '\n%.0s' {1..100}
As Jerry commented you made a syntax error.
This seems to work :
for i in {1..100}
do
echo "\n"
done
Try either of the following
for i in {1..100}; do echo ; done
Or
for i in {1..100}
do
echo
done
Worked for Ubuntu 16LTS and MacOS X
You can abuse printf for that:
printf '\n%.s' {1..100}
This prints \n followed by a zero-length string 100 times (so effectively, \n 100 times).
One caveat of using brace expansion though is that you cannot use variables in it, unless you eval it:
count=100
eval "printf '\n%.s' {1..$count}"
To avoid the eval you can use a loop; it's slightly slower but shouldn't mater unless you need thousands of them:
count=100
for ((i=0; i<$count; i++)); do printf '\n'; done
NB: If you use the eval method, make sure you trust your count, else it's easy to inject commands into the shell:
$ count='1}; /bin/echo "Hello World ! " # '
$ eval "printf '\n%.s' {1..$count}"
Hello World !
One easy fix in Bash is to declare your variable as an integer (ex: declare -i count). Any attempts to pass something else than an number will fail. It may still be possible to trigger DOS attacks by passing very large values for the brace expansion, which may cause bash to trigger an OOM condition.
How do I pass a list to for in bash?
I tried
echo "some
different
lines
" | for i ; do
echo do something with $i;
done
but that doesn't work. I also tried to find an explanation with man but there is no man for
EDIT:
I know, I could use while instead, but I think I once saw a solution with for where they didn't define the variable but could use it inside the loop
for iterates over a list of words, like this:
for i in word1 word2 word3; do echo "$i"; done
use a while read loop to iterate over lines:
echo "some
different
lines" | while read -r line; do echo "$line"; done
Here is some useful reading on reading lines in bash.
This might work but I don't recommend it:
echo "some
different
lines
" | for i in $(cat) ; do
...
done
$(cat) will expand everything on stdin but if one of the lines of the echo contains spaces, for will think that's two words. So it might eventually break.
If you want to process a list of words in a loop, this is better:
a=($(echo "some
different
lines
"))
for i in "${a[#]}"; do
...
done
Explanation: a=(...) declares an array. $(cmd...) expands to the output of the command. It's still vulnerable for white space but if you quote properly, this can be fixed.
"${a[#]}" expands to a correctly quoted list of elements in the array.
Note: for is a built-in command. Use help for (in bash) instead.
This seems to work :
for x in $(echo a b c); do
echo $x
done
This is not a pipe, but quite similar:
args="some
different
lines";
set -- $args;
for i ; do
echo $i;
done
cause for defaults to $# if no in seq is given.
maybe you can shorten this a bit somehow?
When we use shell expansion, it gives all the expanded word in one line. For example:
#!/bin/bash
data="Hello\ {World,Rafi}"
eval echo $data
This produces the following output:
Hello World Hello Rafi
Is it possible to output each line on a separate line like this?
Hello World
Hello Rafi
If I understand you right, you want to generate multiple words using brace expansion ({...}), then print each word on a separate line.
If you don't absolutely have to store "Hello\ {World,Rafi}" in a variable, you can do this with printf shell-builtin
printf "%s\n" "Hello "{Rafi,World}
Some explanation:
The format string (here: %s\n) is reused until all the arguments to printf is used up (Reference).
%s\n consumes 1 argument
"Hello "{Rafi,World} returns 2 words/arguments i.e. "Hello Rafi" and "Hello World"
So, this printf command is equivalent to
printf "%s\n%s\n" "Hello Rafi" "Hello World"
except you don't have to type all that up.
#!/bin/bash
data="Hello\ {World'\n',Rafi'\n',Kamal'\n'}"
eval echo -e "$data"
echo -e will evaluate newline characters.
Same as Antarus'a answer, except that echo has "-n". From http://unixhelp.ed.ac.uk/CGI/man-cgi?echo
-n do not output the trailing newline
#!/bin/bash
data="Hello\ {World,Rafi}'\n'"
eval echo -n -e "$data"
Actually, your problem is not the expansion but the echo command. Depending on your system, you might get what you want by
#!/bin/bash
data="Hello\ {World\\\\n,Rafi}"
eval echo -e "$data"
It is different solution but a very clean one.
#!/bin/bash
names="World Rafi"
for name in $names
do
echo Hello $name
done
Instead of using eval (which is dangerous and really not a good practice — see my comment in your post), another strategy would be to use an array. The following will do exactly what you want, in a clean and safe way:
data=( "Hello "{World,Rafi} )
printf "%s\n" "${data[#]}"
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.
When I try to use the read command in Bash like this:
echo hello | read str
echo $str
Nothing echoed, while I think str should contain the string hello. Can anybody please help me understand this behavior?
The read in your script command is fine. However, you execute it in the pipeline, which means it is in a subshell, therefore, the variables it reads to are not visible in the parent shell. You can either
move the rest of the script in the subshell, too:
echo hello | { read str
echo $str
}
or use command substitution to get the value of the variable out of the subshell
str=$(echo hello)
echo $str
or a slightly more complicated example (Grabbing the 2nd element of ls)
str=$(ls | { read a; read a; echo $a; })
echo $str
Other bash alternatives that do not involve a subshell:
read str <<END # here-doc
hello
END
read str <<< "hello" # here-string
read str < <(echo hello) # process substitution
Typical usage might look like:
i=0
echo -e "hello1\nhello2\nhello3" | while read str ; do
echo "$((++i)): $str"
done
and output
1: hello1
2: hello2
3: hello3
The value disappears since the read command is run in a separate subshell: Bash FAQ 24
To put my two cents here: on KSH, reading as is to a variable will work, because according to the IBM AIX documentation, KSH's read does affects the current shell environment:
The setting of shell variables by the read command affects the current shell execution environment.
This just resulted in me spending a good few minutes figuring out why a one-liner ending with read that I've used a zillion times before on AIX didn't work on Linux... it's because KSH does saves to the current environment and BASH doesn't!
I really only use read with "while" and a do loop:
echo "This is NOT a test." | while read -r a b c theRest; do
echo "$a" "$b" "$theRest"; done
This is a test.
For what it's worth, I have seen the recommendation to always use -r with the read command in bash.
You don't need echo to use read
read -p "Guess a Number" NUMBER
Another alternative altogether is to use the printf function.
printf -v str 'hello'
Moreover, this construct, combined with the use of single quotes where appropriate, helps to avoid the multi-escape problems of subshells and other forms of interpolative quoting.
Do you need the pipe?
echo -ne "$MENU"
read NUMBER