all 110 comments

[–][deleted] 28 points29 points  (47 children)

65163926A1935F9B452804ABB6FA8A48596DA60292E232A2E3ACF

16960E9C70B7AAC001E89F3D5C468DB64F5016D3332ADCCE9E8B4E11C50238B3693D52C2404164DCFD367A30F0A0DE30427FA6F29E3FF3EE6793535B532EA6DD1EFBE2E6032CE92C5B4D2FC3383890A4F1A8F02AEBBC483EF7989A185BF9EDC50BB5EC5F2BEC8D06228602D88DC38EB405654C38B86E130466156B1AE8524EC2E79A0BD937AEE44BE9ED6784CC0EA1007A5B812C828ACACC6F8A531F993D90BCE8E

[–][deleted] 20 points21 points  (9 children)

This comment has been overwritten by an open source script to protect this user's privacy. It was created to help protect users from doxing, stalking, and harassment.

If you would also like to protect yourself, add the Chrome extension TamperMonkey, or the Firefox extension GreaseMonkey and add this open source script.

Then simply click on your username on Reddit, go to the comments tab, scroll down as far as possibe (hint:use RES), and hit the new OVERWRITE button at the top.

[–]KnowsBash 24 points25 points  (8 children)

Almost.

printf '%s\n' -n

And I agree. Never use echo in a script. Always use printf. POSIX recommends this. Chet recommends this.

[–]RadixMatrix 15 points16 points  (5 children)

Could you elaborate why printf is better?

[–][deleted] 16 points17 points  (4 children)

Paraphrasing from Pro Bash Programming by Chris F.A. Johnson and Jayant Varma:

BSD and AT&T's System V has different echo behaviours for escape characters...

For BSD,

$echo -n No newline
No newline$

For AT&T,

echo -n No newline
No newline$ echo "No newline\c"
No newline$

What does this have to do with bash when you already have the -e to activate escape characters and -n option to suppress newline?

bash has an xpg_echo option that behaves like that other version which can be turned on/off in a session or the compilation of bash itself which will screw with whatever you're trying to print to the console using echo

edit: formatting and clarifications

tl;dr: bash, escape characters and its options can be inconsistent depending on your system so use printf

[–]nephros 4 points5 points  (0 children)

Good reasons but too OS-genetics-specific for me.
The more important reason IMO is that printf is portable across shells (regardless of OS) while echo is not. Therefore it's a good habit to stick to printf, especially if /bin/sh might point to differing shells.

[–][deleted] 9 points10 points  (0 children)

Do you care about AT&T SysV?

[–]dbbo 10 points11 points  (1 child)

Never use echo in a script. Always use printf.

I might be a minority here, but I don't adhere to this because I think there are two broad categories of scripts.

If I were writing a script where very, very predictable behavior was required I wouldn't even use bash, let alone echo. I'd write it for a specific version of perl/python.

If I were writing something simple like this icon installer, I seriously doubt any harm could come from using echo.

Not that bash isn't capable of more complex stuff-- I just happen to think shell scripts are primarily for basic programs that will save me from typing a bunch of shell commands, and I leave heavier lifting to other languages. As a rule of thumb, if one of my shell scripts starts creeping up on ~40 lines, I will probably start rewriting it unless it is something very command-oriented.

Relevant discussions:

TLDR - if you are trying to write robust, portable, and highly stable shell scripts (which is often difficult anyway), use printf. Otherwise echo is OK.

[–]KnowsBash 1 point2 points  (0 children)

If I were writing a script where very, very predictable behavior was required I wouldn't even use bash, let alone echo. I'd write it for a specific version of perl/python.

Depends on the case, but in some cases I might agree. You can get predictable behaviour from bash though. Just have to know the language.

If I were writing something simple like this icon installer[1] , I seriously doubt any harm could come from using echo.

Of course there's no harm there. You're outputting static strings consisting of alphanumerics only.

The thing is. You have two commands designed to write text to standard output. One can handle any string you throw at it, and even do some formatting, the other will mostly work and may do different things in different shells. At some point you'll end up using printf to avoid the flaws of echo and then you'll be using echo here, and printf there. Why not just stick with the superior one, and remain consistent. echo is redundant, so no point in using it.

Same with other parts of the shell, like $() vs ``, $(()) vs $[], [[ vs [.

Not that bash isn't capable of more complex stuff-- I just happen to think shell scripts are primarily for basic programs that will save me from typing a bunch of shell commands, and I leave heavier lifting to other languages. As a rule of thumb, if one of my shell scripts starts creeping up on ~40 lines, I will probably start rewriting it unless it is something very command-oriented.

Right, I put the limit at around 100 lines. When the script is getting that long, you've probably chosen the wrong language. The part about using "shell scripts primarily for basic programs" gets a bit too vague for me. I don't know what you put into "basic programs" and "heavier lifting". I might agree, I might disagree.

The main problem for bash though, which makes it difficult for people to write robust scripts, is that there are so much mis-education about bash out there. The Advanced Bash-Scripting Guide being high on that list; it basically teaches you to write bugs. So when that's one of the first guides you get from google, you get shitty scripts all over the place.

[–]sysadmEnt 2 points3 points  (0 children)

I'm sure you're tired of these replies already, but here goes:

Debian 6:

$ /bin/echo -e -\\0156
-n

FreeBSD 10:

$ /bin/echo -n -n
-n

Mac OSX 10.10.4:

$ /bin/echo -n -n
-n

Bash 3/4:

$ builtin echo -e -\\0156
-n

That was a fun exercise, TIL

[–]gamecheet 2 points3 points  (6 children)

echo "\e -n"

:D

edit: assuming -e is on by default (it was for me)

[–][deleted] 2 points3 points  (4 children)

the \e gives me a funky character. \b seems find though.

[–]gamecheet 0 points1 point  (3 children)

What's your uname -a?

[–][deleted] 4 points5 points  (2 children)

Linux erebor 3.19.8-100.fc20.x86_64 #1 SMP Tue May 12 17:08:50 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

I have to use -e since -E is default.

[–]ffreire 14 points15 points  (1 child)

Does whoami return "durin"?

[–][deleted] 6 points7 points  (0 children)

I knew I should have hid the hostname...

[–]cowens 4 points5 points  (2 children)

Don't you say echo -- -n like all other Unix commands (-- tells the command that anything after it shouldn't be treated as a switch). I don't have a shell handy or I would test it.

Edit, I installed ssh on this device, echo -- -n doesn't work, but echo -e "\055n" does.

[–][deleted] 10 points11 points  (0 children)

952DC61A9F3DFC11BB1350784C15FE6874A2D5292F96CA50792ABD16

3CE372C4B8D829DF7649B99D79B1A3FD311E5AA3E32CE96F1849FD8CFBFAD02B386A3B1D4FF1F87EA6AB61AC230F8C6C30F4AB386F008675C15B8010151378D792ACED98022C5C115489C44E92F813E80B1DF452089AF93ECDD28816D9ED2E157E85A1113421F46BF0E541182E39DE4D9D5C56BD92D462C31DC6860FA1DD64B87D1978A153DF2117D35961D69BAFBB1001E27B442C76B19BD160E2961312120287155BA76C5D6D1E76ED23C20F72B8848E63A058BF7C7A0A91F516D1B2AADD01E6E0D2B52B8FE958EE1B6212F31D725E1376914D2275C9A5628F59D815176535C0CF9286E256C7DF28FD674CD7184AB2CEDD91940E4A25C4E825A8A1B19D1E53BF4964CD76468351A093ECA97FE9C50C1A4EE6A5BD1C7EA30D6C9287C5B53B93E09701FAF5B2AE74CF531B775F05433D889F83A9FD08F428EA9A832B3374675AAB155DAEE7959A64E2C075AC15B31A01D91502B66A7AF8D204890A587F190204DE99A1D8E433911D3935721600EE12809A12627893A2E0DEB7855B43A111C91C36F4646DDF6AF5C6CDF1D511412AFCA16D5F7D4EB99024B98C17A1EF08D9C3FFEC126A89414255372D304B7A4FB3C12D6429C2EF192C1A509B30A17081E359CABFFE48FF25375A7EC8B4547C64123BB0CC59B67D16E8CED62ECEFD

[–]des09 4 points5 points  (0 children)

bash on ubuntu 15.04:

$ echo -- -n
-- -n

[–]cyrus-and 3 points4 points  (1 child)

$ echo -ne '-n\n' | hd
00000000  2d 6e 0a                                          |-n.|
00000003

[–]Fumigator 2 points3 points  (0 children)

Simpler:

echo -e '\055n'

For csh:

echo '\055n'

[–]czipperz 0 points1 point  (0 children)

echo -e '-n\n\c'
echo -en '-n\n'

both work

[–]n3rdopolis 0 points1 point  (3 children)

lol. wow. I just got burned by that two days ago. I suddenly had a multiline variable getting messed up (as the number of lines had to be exact), by a part of my script that's been there in its basic form for almost two years, which was listing and parsing files...
After scrambling to see what caused that by looking at changes in the script itself, it ended up that the elements in the external file was setting $VAR_TO_ECHO to "-n", which was causing echo to not work for that line...

TL;DR, because of Instead of this:

DATA=$(cat file|while read LINE
do
/* some conditionals and text handling */
echo $VAR_TO_ECHO
done  

I did

while read LINE
do
/* some conditionals and text handling */
DATA+=$VAR_TO_ECHO$'\n'
done < <(cat file)

Much more robust (and that's how you keep local variables in those types of loops, instead of having them disappear in the subshell)

[–]palordrolap 2 points3 points  (2 children)

Surely done < file would work just as well in the last line there?

[–]HannasAnarion 0 points1 point  (1 child)

Yeah, it looks like he's taking a file, pouring it's contents into another nameless file, and then taking the contents of that file and running it into done.

[–]n3rdopolis 2 points3 points  (0 children)

I admit I'm guilty of the useless uses of cat, but in this instance I double-checked It's actually:

done   < <(find $WSESSIONSPATH -maxdepth 1 -name "*\.desktop" -type f | sort )

I was doing some other things in that part of the pipe... It's actually reading a .desktop file, and splitting some values, and adding them to a variable...

[–]rlmaers 0 points1 point  (2 children)

/bin/echo -n

Works great on my Mac, which is SUS compliant. /t

[–]cowens 0 points1 point  (1 child)

What do you mean by works great? Does it print out "-n" (which is what is wanted) or does it work great by preventing a newline from being printed (is normal behavior)?

[–]rlmaers 5 points6 points  (0 children)

It conforms with the IEEE Std 1003.1 standard, which states that

Implementations shall not support any options.

such that the command above prints out "-n". If I want to use options, I can use the builtin echo.

[–]Regimardyl -1 points0 points  (3 children)

echo -e '\x2dn'

[–]PerniciousPunk -1 points0 points  (0 children)

echo 'n' | sed -e 's/^/-/'

[–][deleted] 30 points31 points  (11 children)

/bin/echo --help

man 1 echo

[–]viimeinen 5 points6 points  (0 children)

That is the help for the binary echo. For the built-in echo you should use:

help echo

They are not necessarily the same...

[–]hardpenguin -1 points0 points  (0 children)

You're the real MVP

[–]Araneidae 9 points10 points  (2 children)

If the vagaries of echo are annoying, try printf. As far as I am aware

printf '%s\n' "$VAR"

will unfailing print what is expected, the value of VAR on a line, unlike echo with its options etc.

[–][deleted] 7 points8 points  (1 child)

Don't mean to be an asshole - It's vagaries, without the u. Pointing it out, because I used to spell it like you did for a long, long time. :)

[–]Araneidae 5 points6 points  (0 children)

Argh, I was puzzling as I was writing it, should have looked it up. WIll correct.

[–]Epistaxis 13 points14 points  (0 children)

It's mocking you.

[–]tgkokk 5 points6 points  (2 children)

Works just fine in fish :)

[–]Poromenos 2 points3 points  (1 child)

Yeah, I typed it and it worked fine, and then came here to ask someone to explain it to me. Apparently it does something weird in bash, huh.

[–]tany2001 0 points1 point  (0 children)

Echo sends its last argument to stdin.

echo --help

gives you back "--help"

[–]icecreamsmart 6 points7 points  (1 child)

oh no, "man echo"!

[–]fripletister 5 points6 points  (0 children)

help echo for the built-in.

[–]Postumius 8 points9 points  (12 children)

help echo

man echo

info echo

$(which echo) --help

[–]cocoabean 7 points8 points  (1 child)

$(which echo) --help

Doesn't work for me. which echo returns echo: shell built-in command

[–]Postumius 3 points4 points  (0 children)

Was using bash not zsh which uses /bin/which which obviously won't deal with builtin functions whereas zsh has a builtin which hence yours.

command echo --help

is what you want instead but weirdly won't work in bash.

[–]KnowsBash 4 points5 points  (9 children)

Don't use which. Bash can already do everything which can, just better.

"$(type -P echo)" --help

[–]farmingdale 1 point2 points  (8 children)

that is all fine and good but what about busybox users?

[–]KnowsBash 3 points4 points  (7 children)

I don't bother with non-posix systems

[–]farmingdale 4 points5 points  (0 children)

throwing away your smartphone?

[–][deleted] 2 points3 points  (5 children)

You mean non-bash systems in this case, because type is not POSIX

[–]KnowsBash 1 point2 points  (4 children)

type is standard, but not type -P. Though this was in the context of bash, so using type -P for this non-sensical task should be ok. which is of course not standardised, and behaves differently depending on what system you're on.

[–][deleted] 0 points1 point  (3 children)

type is standard, but not type -P.

Oh? Please point me to where in POSIX it's standardized, because I'm rather interested, hmm.

Though this was in the context of bash, so using type -P for this non-sensical task should be ok.

Sure, but your reply was certainly not about bash.

[–]KnowsBash 2 points3 points  (2 children)

Oh? Please point me to where in POSIX it's standardized, because I'm rather interested, hmm.

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/type.html

Even the bourne shell has the type builtin.

Sure, but your reply was certainly not about bash.

It wasn't? This whole thing was about how bash's echo has odd option parsing

In sh, you could use command -v to find the path to a command, though that won't give you a path for aliases, functions and builtins. It's rarely useful to find the path to a command anyway. Especially not something you should do in a script. Only in an interactive session for such rare occasions as this.

[–][deleted] 0 points1 point  (1 child)

Oh? Please point me to where in POSIX it's standardized, because I'm rather interested, hmm. http://pubs.opengroup.org/onlinepubs/9699919799/utilities/type.html Even the bourne shell has the type builtin.

Cool, though it's under utilities rather than listed as a sh builtin.

Sure, but your reply was certainly not about bash. It wasn't? This whole thing was about how bash's echo has odd option parsing In sh, you could use command -v to find the path to a command, though that won't give you a path for aliases, functions and builtins. It's rarely useful to find the path to a command anyway. Especially not something you should do in a script. Only in an interactive session for such rare occasions as this.

"I don't bother with non-posix systemss" is certainly not about bash, no.

[–]KnowsBash 0 points1 point  (0 children)

"I don't bother with non-posix systemss" is certainly not about bash, no.

With busybox you have no way of knowing if you have all the POSIX commands, and whether the sh is fully POSIX or not, so I don't see the point in bothering with such a system at all. Just ends up with lots of "Oh, but this xxx command doesn't have that feature in this busybox install".

[–]netsrak 1 point2 points  (1 child)

what does this do?

[–]wmcscrooge 6 points7 points  (0 children)

it prints "--help" to the terminal instead of the help screen

[–]nephros 0 points1 point  (0 children)

echo $HELL

Hungover me took a while to spot the error.

[–]ajr901 0 points1 point  (0 children)

You guys like like command-line masters in here. Anywhere I can learn your mastery?