2

If I type echo hello, my terminal looks like this:

My-Computer:~ user$ echo hello
hello
My-Computer:~ user$ 

But if I have a function like this

stringen () { cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-Z-A-z-0-9' | head -c "$1" ; }

It looks like this

My-Computer:~ user$ stringen 18
^\k6JPWVIUdEiudcWSMy-Computer:~ user$ 

See that it doesn't print it in a new line? Same thing when using printf vs echo. How can I do so that my functions makes new lines like this? I thought that the ; would take care of this but obviously not.

For example, this returns what I want

variable=$(stringen 18)
echo $variable

Is it something with echo?

4

Neither, cat, tr or head adds a newline character (urandom could, but it's random so I wouldn't trust it). You have to manually insert the new line, with either a printf '\n' at the end of your function, or an echo.

BTW, some shells do add it, like zsh, to prevent these kinds of situations.

  • Absolutely right! I would add a couple of points: (1) The function can be reduced to { LC_CTYPE=C tr -cd A-Za-z0-9 < /dev/urandom | head -c "$1"; } (using syntaxerror’s correction to the tr arguments). You can put the A-Za-z0-9 into quotes if you want to, but it’s not necessary. (2) The OP’s tr arguments were more badly broken than syntaxerror said. A-Z-a-z-0-9 means A through Z, and -, and a through z, and - again, and 0 through 9. – Scott Dec 4 '14 at 23:57
  • @Scott: Read the original tr argument thoroughly again: it was not really "A-Z-a-z-0-9" but a-Z-A-z-0-9, big letters alternating with small ones. It's not exactly the same thing. So in fact GNU tr obviously interpreted a part of it as ...Z-A..., thus in reverse - and got totally confused as I demonstrated. – syntaxerror Dec 5 '14 at 0:25
  • @syntaxerror: I read the original tr argument correctly. Sorry if I was unclear. I was making the point that (character)-(character)-(character)-(character)-(character)-(character) was a flawed construct in addition to a-Z being an error. And I find it a little funny that you say, “… GNU tr obviously interpreted a part of it as ...Z-A..., … as I demonstrated.” after you said “Hey, how could I be so sure to blame a command with 100% probability? You never can.” You demonstrated no such thing; the error message that you showed says that “a-Z” is the problem, not “Z-A”. – Scott Dec 5 '14 at 20:38
  • Well, when I got that error message on my terminal first time, I seriously thought it might be something in-between of what you pointed out. ;) Anyways...please consider that error messages from system commands may quite frequently be way off the mark, leading you up the garden path. – – In a nutshell, chances were that tr took the line as-is and parsed it for the first correct bit in it from left, which was indeed Z-A. So I thought it had read over the a- and pretended it wasn't there, just cherry-picking what it could interpret properly. (No it wasn't much.) – syntaxerror Dec 5 '14 at 20:52
4

It doesn't matter in the least if your function prints a newline or not. Command substitution will remove them if there are any present.

Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. (emphasis mine)

testfunc() { echo hello; }    # a newline is printed
output=$(testfunc)            # $output does NOT contain a trailing newline
echo "$output"                # echo adds a newline
printf "%s" "$output"         # no newline
0

IMHO, this might partly have to do with echo but there's another part of the problem taking influence. So the ideal situation would be if your distro did ship a recent GNU version of tr (like in mine), because as you can see below, there is a clear quirk in your syntax and GNU tr complains to show you where you have to apply the "fix".

original:

$ stringen() { cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-Z-A-z-0-9' | head -c "$1" ; }

$ stringen 18
tr: range-endpoints of 'a-Z' are in reverse collating sequence order

Ouch! Here is an alternate version of your line which fixes it (at least in my distro with GNU tr it did the trick):

"fixed":

$ stringen () { cat /dev/urandom | env LC_CTYPE=C tr -cd 'A-Za-z0-9' | head -c "$1" ; }
$ stringen 18
V068BrYGKIICyLI4QB
$ 

Conclusion: you had better not mix sets with tr, but when combining sets/ranges, always make sure that you keep big letters to big letters and small letters to small letters.

However: With a non-GNU tr, the problem with the missing newline might be still persisting. But with a GNU one, my corrected line will no longer make any fancy newline trickery necessary.

  • What version of tr we are talking about? In my system, it isn't adding the newline at the end. – Braiam Dec 4 '14 at 23:27
  • Oh, good point. Mine is GNU tr from coreutils v8.23. But anyway: I think this line will also work with non-GNU tr, just ... the GNU one appears to be the winner because it shows the user where his syntax problem is and how he can fix it. – syntaxerror Dec 4 '14 at 23:29
  • @Braiam There we go again. Thanks to your considerations, I felt like improving my answer w/strictly distiguishing between GNU and non-GNU tr. – syntaxerror Dec 4 '14 at 23:36
  • 3
    IMNSHO, any version of tr that would add a newline in this context is broken. Are you sure it’s tr that’s doing it? (How could tr possibly be doing it? It’s almost surely head that’s doing it. Still a bug, IMO, but not as terrible a one.) While the OP’s arguments to tr were clearly wrong, they had nothing to do with his problem. – Scott Dec 4 '14 at 23:56
  • 5
    This has everything to do with echo: it prints its argument followed by a newline. tr works on binary data and printing an extra newline would be a bug. head -c can't print an extra newline either, it strictly prints the requested number of bytes. If you're seeing a newline it must be because your shell is configured to move to the next line before displaying a prompt. Try stringen; echo more stuff. – Gilles Dec 5 '14 at 0:51
0

You don't need a variable to save the output for this. Simply echo the output directly :

echo "$(stringen 18)"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.