LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Handling newlines in Bash (https://www.linuxquestions.org/questions/programming-9/handling-newlines-in-bash-583396/)

MS3FGX 09-09-2007 11:10 AM

Handling newlines in Bash
 
I have a slight aesthetic problem that I can't seem to wrap my mind around, and I was hoping I might get some input on it here.

Basically, I want to be able to print a status message, execute a command, and then follow it with a message about it succeeding on the same line. That is easy enough to do, I just have something like this:

Code:

echo -n "Running command..."
command 2>/dev/null
echo "Done!"

That works fine, but the problem comes in when the task fails. In my Bash scripts I use an error handler, so it actually looks like this:

Code:

ErrorMsg ()
{
# ErrorMsg <Error Type> <Error Message>
# Displays either a minor (warning) or critical error, exiting on an critical.
if [ "$1" == "ERR" ]; then
        # This is a critical error, game over.
        echo "  ERROR: $2"
        exit 1
elif [ "$1" == "WRN" ]; then
        # This is only a warning, script continues but may not work fully.
        echo "  WARNING: $2"
fi
}

echo -n "Running command..."
command 2>/dev/null || ErrorMsg ERR "The command has failed!"
echo "Done!"

Functionally, this works. But the problem is that since I printed the first line "Running command..." without the newline, my error message follows it instead of being below and to the right as I want.

So it looks like:

Code:

Running command...  ERROR: The command has failed!
Instead of:

Code:

Running command...
  ERROR: The command has failed!

Now, the obvious solution was to change my echo lines in ErrorMsg () to:

Code:

echo -e "\n  ERROR: $2"
BUT, here is the problem. I also want to call ErrorMsg in situations where I have not previously printed a line without a newline, in which case, using this method makes the error print a blank line first.

So basically, my problem is that I want to be able to print the error line where I want it, in either situation. It seems that I need some method of detecting if the previous line was ended or not, and then act accordingly.

My other idea was to give the newline command to ErrorMsg, so it would look like:

Code:

ErrorMsg ERR "\nThis is an error."
But because of the way I am handling it, this won't work properly since the "ERROR:" part is always printed regardless of the line I give it to print.

So it comes out like:

Code:

ERROR:
This is an error.

Terrible.

I was thinking that I could do something where I check if the first characters of the string are "\n", then strip them adjust the echo command accordingly? Like:

Code:

if $string starts with \n; then
    strip \n from $string
    echo -e "\n ERROR: $string"
else
    echo " ERROR: $string"
fi

Is there a better way to handle this issue? I don't actually know how to strip the characters from the string and such, but I am sure it can't be too difficult. I just want to know if there is a better method to work this out before I start playing around with my own concept.

P.S.

Yes, I am aware this is a rather ridiculous question in the grand scheme of things, but I am a bit obsessive and it bothers me that my error message don't all look the same with the current method.

druuna 09-09-2007 11:36 AM

Hi,

I've been trying to duplicate your problem and I can't (using single command to substitute command 2>/dev/null part).

The only thing unclear seems to be this 'command' part of the script, could it be that this is the 'problem'? Could it be that besides the message sent to stderr there's also something going to stdout (piped commands come to mind)?

Ok, that doesn't solve your problem but could point to the reason for this behavior.

If you want cursor position control you could take a look at tput. It's probably a bit more work to implement, but if you like aesthetic output it could be worth the effort.

Hope this helps.

ilikejam 09-09-2007 11:49 AM

Why not addan argument to ErrorMsg to tell it whether or not to print a newline before the error?

Dave

MS3FGX 09-09-2007 12:31 PM

I was considering that as well, simply make a third variable that acts as a bool determining whether or not to do a newline. I was looking for something a bit more automated, if nothing else for the sake of elegance, but that is certainly a fallback option; and really my best one I suppose. Easier than playing around with stripping the newlines off the string and such,

Quote:

I've been trying to duplicate your problem and I can't (using single command to substitute command 2>/dev/null part).
Perhaps the command you were trying wasn't failing? It won't run ErrorMsg unless it fails. As an example, here is what happens when we run a command that will definitely fail (you could do the same with just "false", but I didn't think of that until just now):

Script:
Code:

ErrorMsg ()
{
# ErrorMsg <Error Type> <Error Message>
# Displays either a minor (warning) or critical error, exiting on an critical.
if [ "$1" == "ERR" ]; then
        # This is a critical error, game over.
        echo "  ERROR: $2"
        #exit 1
elif [ "$1" == "WRN" ]; then
        # This is only a warning, script continues but may not work fully.
        echo "  WARNING: $2"
fi
}


# In the real usage, the "Done!"'s would never print (ErrorMsg would bail out),
# but there are here for illustration, since in the real script the commands
# should be succeeding

clear
echo "This is what happens when there is no newline:"

echo -n "Running bogus ping..."
ping -w 1 1.0.0.0 >/dev/null || ErrorMsg ERR "The command has failed!"
#echo "Done!"

echo
echo "This is what it should look like:"

echo "Running bogus ping..."
ping -w 1 1.0.0.0 >/dev/null || ErrorMsg ERR "The command has failed!"
#echo "Done!"

Output:
Code:

This is what happens when there is no newline:
Running bogus ping...  ERROR: The command has failed!

This is what it should look like:
Running bogus ping...
  ERROR: The command has failed!


druuna 09-09-2007 01:30 PM

Hi,

Quote:

Perhaps the command you were trying wasn't failing?
Yep, they failed.

Your second example isn't the same as the first (stdout vs stderr):

First:
command 2>/dev/null || ErrorMsg ERR "The command has failed!"

Second:
ping -w 1 1.0.0.0 >/dev/null || ErrorMsg ERR "The command has failed!"

The second example fails to create the correct error message on both ping commands.

If I change the >/dev/null to 2>/dev/null, only the second ping fails to create the correct error message (I removed the clear command):
Code:

$ ./error.on.same.line.2.sh
This is what happens when there is no newline:
Running bogus ping...  ERROR: The command has failed!

This is what it should look like:
Running bogus ping...
  ERROR: The command has failed!

Which bash version are you using?

$ bash --version
GNU bash, version 3.1.17(1)-release (i686-pc-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

jozyba 09-09-2007 01:40 PM

Does this work the way you want it to?
Code:

ErrorMsg ()
{
# ErrorMsg <Error Type> <Error Message>
# Displays either a minor (warning) or critical error, exiting on an critical.
if [ "$1" == "ERR" ]; then
        # This is a critical error, game over.
        echo "  ERROR: $2"
        #exit 1
elif [ "$1" == "WRN" ]; then
        # This is only a warning, script continues but may not work fully.
        echo "  WARNING: $2"
fi
}

clear

echo -n "Running a successful command..."
ls 2>/dev/null >1 && echo "Done!" || (echo; ErrorMsg ERR "The command has failed!")

echo

echo -n "Running a failing command..."
ping -w 1 1.0.0.0 >/dev/null && echo "Done!" || (echo; ErrorMsg ERR "The command has failed!")


MS3FGX 09-09-2007 02:38 PM

Quote:

Does this work the way you want it to?
Yes, that is exactly it. I knew I was missing something obvious here. It may not be too elegant, but it definitely works.

Thanks a lot.

chrism01 09-10-2007 10:15 PM

Re your newline issue, we've prob all been there if writing large progs/lots of code.
My (eventually developed) rule is that the msg output fn should do that and no more, in the spirit of the Unix philosophy of KISS and no side-effects/suprises.
It's up to the caller to format the way the msg is laid out.
Of course in C, Perl that's fairly easy...


All times are GMT -5. The time now is 01:13 PM.