-1

How can the white space character be escaped in printf for the purpose of this script that currently runs in a Cygwin client? The line containing the whitespace (which by itself works from the command line as echo $SPC) is

SPC=$(printf \\$(printf '%03o' 32))

In case you might wonder why, in this code I am trying to simulate an associative array such that it is not dependent on Bash 4+...would like this to be a generic example that theoretically can be run in virtually any bash environment.

Here is the entire script:

#!/bin/bash

DEFAULT_INDEX=""
LAST_INDEX=0
SPC=$(printf \\$(printf '%03o' 32))

# MICROSOFT OUTLOOK VERSIONS.
OUTLOOKS=( "Cancel^Do$SPCnot$SPCtry$SPCto$SPCsend$SPCmessage$SPCvia$SPCOutlook"
           "URL^Just$SPCthe$SPCmailto$SPCURL"
           "Outlook$SPC2003^C:\Program$SPCFiles\Microsoft$SPCOffice\Office12"
           "Outlook$SPC2007^C:\Program$SPCFiles\Microsoft$SPCOffice\Office12"
           "Outlook$SPC2010^C:\Program$SPCFiles\Microsoft$SPCOffice\Office14" )

# ITERATES THROUGH ARRAY AND MAKES NUMBERED SELECTION LIST.
printOutlooksArraySelectionList() {
    LIST_TITLE="MICROSOFT OUTLOOK SELECTION LIST"
    echo
    printf "\t\t\E[37;1;44m%-0s %s %s %s\033[0m" $LIST_TITLE
    echo
    echo
    DEFAULT_VALUE="Outlook 2010"
    index=0
    for i in "${OUTLOOKS[@]}"; do
        key=${i%%^*}
        value=${i##*^}
        if [ "$key" == "$DEFAULT_KEY" ]; then
            DEFAULT_INDEX=$index
    fi
            printf "\t\E[37;44m%-3s %-13s\033[0m \E[1;34;40m%-41s\033[0m\n" \
                   $index $key $value
            echo
            ((index++))
    done
    # SETS LAST_INDEX TO INDEX VALUE OF LAST ITEM IN ARRAY.
    ((LAST_INDEX = index - 1))
}

printOutlooksArraySelectionList

Would like the output to look like the following:

            MICROSOFT OUTLOOK SELECTION LIST

    0   Cancel        Do not try to send message via Outlook

    1   URL           Just the mailto URL

    2   Outlook 2003  C:\Program Files\Microsoft Office\Office12

    3   Outlook 2007  C:\Program Files\Microsoft Office\Office12

    4   Outlook 2010  C:\Program Files\Microsoft Office\Office14

To work-around, I ended up not using printf, but instead:

echo -e "\t\E[37;44m${index}\t${key}\033[0m\t\t\E[1;34;40m${value}\033[0m"

adequate for my purposes with this script.

amphetamachine
  • 5,517
  • 2
  • 35
  • 43

3 Answers3

2

I don't understand what you're trying to do. Your code does set SPC to a string of length 1 whose single character is a space. This is a highly convoluted way of doing it: SPC=" " would work just as well and be comprehensible.

I haven't reviewed your whole script, but I do see something wrong later down: you write things like "URL^Just$SPCthe$SPCmailto$SPCURL". This produces the string URL^Just, because the variables $SPCthe, $SPCmailto and $SPCURL are not defined. When a variable expansion is immediately followed by a character that could be part of the variable name, you need to indicate the end of the variable name, for example by putting the name into braces: URL^Just${SPC}the${SPC}mailto${SPC}URL. I don't understand why you're not writing "URL^Just the mailto URL", though.

For your encoding of small hash tables, I suggest using two array, once for keys and one for values. That way, you can store arbitrary strings, without fearing encoding issues. Here's a sketch of what lookup and addition can look like (taking the names of the keys and values arrays as parameters is left as an exercise):

lookup () { # $1=key; set value to the value found
  for ((i=1; i<=${#keys}; i++)); do
    if [[ ${keys[$i]} = $1 ]]; then value=${values[$i]}; return 0; fi
  done
  unset value; return 1
}
add () { # $1=key $2=value
  for ((i=1; i<=${#keys}; i++)); do
    if [[ ${keys[$i]} = $1 ]]; then values[$i]=$2; return; fi
  done
  keys[$i]=$1; values[$i]=$2
}

A more efficient method would be to store each entry in a shell variable. For example, if the table foo maps somekey to somevalue, then set the variable table_foo_somekey=somevalue. You need to encode the keys into alphanumerics, which is non-trivial (one method is to hash the keys, e.g. with sha1, and store the key in table_foo_${sha1_of_somekey}_key and the value in table_foo_${sha1_of_somekey}_key). If you need to enumerate the keys in a table, you need to store the list separately.

Or, when you need things that go beyond the capabilities of a simple shell, you could reach for a more powerful tool. Ksh93 (not pdksh), bash 4 and zsh all have associative arrays. Beyond that, there's Perl and Python and the rest of them.

  • Thank you for the comments, suggestions, and information...I changed the assignment: SPC=" " but the result is still not as expected. Initially, I tried the '"URL^Just the mailto URL"' syntax, but it does not work as expected in Cygwin, hence the attempts at using escape characters that represent the whitespace character. – mikequentel Nov 18 '11 at 04:55
  • 1
    But what are you expecting from SPC? – ДМИТРИЙ МАЛИКОВ Nov 18 '11 at 06:52
  • 1
    @MikeQuentel I guarantee you that your SPC variable will not help with whatever problem you're having. The space character is not different on Cygwin. Remove it, which will fix some of the problems with your script as I've explained. Then, if you still need help, post your updated, simplified script, and explain what the script does, what you expect from the script, and how they differ. – Gilles 'SO- stop being evil' Nov 18 '11 at 08:11
1
  • Since you've already used quotes around the strings, using $SPC in them is not necessary and gives you no benefit. You don't need any definition of space there.

  • As Gilles pointed out, strings like "URL^Just$SPCthe$SPCmailto$SPCURL" contain non-existing variables. A variable ends either at a character that cannot be used in any variable name, or when it is (properly) delimiterred with {} as in ${key}.

  • Thank you, rozcietrzewiacz; your suggestion to put the SPC variable into ${SPC} solved the problem. – mikequentel Nov 18 '11 at 12:46
  • Thanks for accepting, but you really should just get rid of the $SPC variable instead. It is useless - just put spaces in there. – rozcietrzewiacz Nov 18 '11 at 13:03
  • When I just put spaces in the text, the script does not behave as expected in Cygwin. The script works without the SPC variable (as you suggest getting rid of $SPC) in a true Linux environment like Ubuntu 11.04 (just tested it in that environment). However, have not gotten it to work in Cygwin on Windows XP. – mikequentel Nov 18 '11 at 15:46
  • This sounds very strange - you might want to investigate this further (use a simplest script possible). There might be some other error that causes this. – rozcietrzewiacz Nov 18 '11 at 15:50
  • After further investigation, I think there is indeed something else that was causing the error in Cygwin. I again tested using just " " (no SPC variable) in the script, in Cygwin, and now it works. Could be something about my workstation configuration or some other factor. Weird. Again, thank you for pointing out the variable expansion aspect, in general. – mikequentel Nov 18 '11 at 16:18
  • @MikeQuentel A common problem on Cygwin is that Windows text files use CR,LF for newlines, but Cygwin tools treat the LF as a newline and the CR as an ordinary character. Maybe your script or the data you're processing has a stray CR somewhere. I am not going to speculate as to where your overly complex script might be going wrong. – Gilles 'SO- stop being evil' Nov 18 '11 at 17:56
  • I've been editing in Notepad++, then running dos2unix in Cygwin. – mikequentel Nov 18 '11 at 19:45
0

I've already mentioned some misunderstanding of that problem. But anyway.

The line containing the whitespace (which by itself works from the command line as echo $SPC) is

SPC=$(printf \\$(printf '%03o' 32))

Actually, that line do not containing any whitespaces. It expands in

printf \\040

which expands in nothing good. And printfing a printf output is a really bad idea at all.

Print could show space symbols if you want it:

$> printf '  %03o\n' 32
  040

Leading nulls and leading zeros too:

$> printf "%010o\n" 32
0000000040

$> printf "% 10o\n" 32
        40

UPD by comment:

$> printf '\\%03o\n' 32
\040
  • Thank you for the clarification. Similarly, I had previously tried using the value of printf "\040" (octal value) which works at the command line by itself, but using the octal does not work with the script. – mikequentel Nov 17 '11 at 21:53
  • The line SPC=$(printf \\$(printf '%03o' 32)) does set SPC to a single space character, in a highly convoluted way. While I agree with your “bad idea” characterisation, this bit does work as (possibly) intended. – Gilles 'SO- stop being evil' Nov 18 '11 at 10:37