1

I have the following in my .bashrc file I use for a log:

function log(){

  RED="\e[0;31m"
  RESET="\e[0m"
  echo -e "${RED}$(date)" "${RESET}$*" >> "$HOME"/mylog.txt
}

But when I do something with an apostrophe in it, it comes up with some sort of prompt and does not log it properly.

How do I escape all the text being input into the file?

Example:

$ log this is a testing's post
> hello
> 
> ^C
$

Thanks.

7

The problem you have has nothing to do with echo -e or your log() function. The problem is with apostrophes:

log this is a testing's post

The shell (bash, in your case) has special meanings for certain characters. Apostrophes (single quotes) are used to quote entire strings, and prevent most other kinds of interpolation. bash expects them to come in pairs, which is why you get the extra prompt lines until you type the second one. If you want a literal single quote in your string, you need to tell bash, by escaping it via \', like so:

log this is a testing\'s post

Again, log is beside the point. You can try this out with plain old echo if you like:

echo this is a testing\'s post

See Which characters need to be escaped in bash for more info.

  • Is there a way to achieve the functionality of my log script without having to escape any characters? – Proletariat Dec 1 '15 at 10:03
  • You can use double quotes, as in log "it's my life", but that runs into similar problems if you'll ever have double quotes in your string (or any other characters that need to be escaped (see the question I linked in my answer). This is a problem with shell commands in general. You can use input redirection or here documents, but then you no longer have a one-line command. Can you tell me more about how your log command will be used? – type_outcast Dec 1 '15 at 11:51
  • Basically, from the terminal, log the stuff isn't working. Then it will input the text after log into a text file with date and timestamp. – Proletariat Dec 1 '15 at 11:58
  • 1
    OK, fair enough. Assuming you're logging one message at a time (with other commands in between your log entries), there isn't a whole lot more you can do. For casual things like this, I've personally just gotten used to using quotes and escaping, depending on the specific text in each message. E.g., I'd use log Plain old string vs. log "Don't look" vs. log 'This "job" is too much fun' vs log I could sure use \$20. Unfortunately, if that bit of extra typing is unacceptable for you, then you would need to modify your requirement of being able to type the messages at a bash shell prompt. – type_outcast Dec 1 '15 at 12:14
  • 1
    For example, with some work to log itself, you could use here documents, which would mean log <<EOF<Enter>here's my entry<Enter>EOF<Enter> It's more typing than simply escaping, though. Similarly, you could use stdin like log<Enter>here's my entry<Enter><Ctrl-D> Again, more typing. You could make log interactive, so you type a line and hit <Enter>, but it stays running so you can just leave it go in a separate terminal. You could find or write your own custom shell that has simpler escaping. Many options. – type_outcast Dec 1 '15 at 12:26
5

Your problem is because you entered a command line that contained an unmatched single-quote character. It has nothing to do with echo. The shell is issuing a secondary prompt to let you know that it is expecting the end of the single-quoted string started by 's po...

You'd need to escape that ' if you want it passed litterally to the log function:

log "this is a testing's post"

Or:

log this is a testing\'s post

for instance.

Now, where you'd need to escape characters from echo -e is for the backslash characters. As for instance if you call it as log '\begin', that \b would be translated to a BS character by echo -e.

To address that, either store the escape sequences with those \e expanded in those variables:

log() {
  RED=$'\e[0;31m'
  RESET=$'\e[0m'
  printf '%s\n' "${RED}$(date) ${RESET}$*" >> "$HOME"/mylog.txt
}

(here using bash, ksh93, mksh, zsh or FreeBSD sh syntax for that not-yet-POSIX $'...' syntax).

Or use this syntax:

log() {
  RED='\e[0;31m'
  RESET='\e[0m'
  printf '%b%s%b %s\n' "$RED" "$(date)" "$RESET" "$*" >> "$HOME"/mylog.txt
}

Note that the expansion of "$*" depends on the current value of $IFS.

In any case, it's better to avoid echo for arbitrary data.

  • Instead of expanding \e in the variables, the OP would be better advised to use the tput program to generate the correct escape codes for their particular terminal. Not all the world is a VT220! – Toby Speight Dec 1 '15 at 11:50
  • @TobySpeight, but 99.99 of the world that supports tput setaf 1 implements it like that and the rest would probably just ignore the sequence. Not to mention broken terminfo databases or systems that don't have tput or tput implementations that recognise only termcap names. Ideally, you may want to treat terminals that use setf or colour-pairs differently... All that is probably overkill. See also unix.stackexchange.com/a/219663 – Stéphane Chazelas Dec 1 '15 at 11:57

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.