Age of Reason

Random musing of books and stuff I am reading.

2005/01/04

Double Quoted Shells.

Ever had trouble putting quotes inside bash aliases?
Then read on, for Bash and sh allow extra ordinary
levels of quotations. The examples below would be
unthinkable in csh and other simplistic shells.

Of course DOS users would rather drag and drop
filenames with spaces in them.



To avoid the disease quototitis we will use the
abbreviations dq (") and sq('). Note that dq
inside sq strings are ignored.

1. To expand a shell variable inside a 'string':
1.1 End the sq string.
1.2 Put the variable in dq (to prevent extra spaces),
1.3 Start sq string again.

> perl -e 'print("...'"$HOME"'...\n");'
...c:/mosh...

2. To insert sq inside a sq-string:
2.1 close sq string.
2.2 put standalone backslashed sq
2.3 start sq string again.

> perl -e 'print "I just can'\''t do that!\n";'
I just can't do that!

> echo 'aaa'"'"'bbb'
aaa'bbb

3. To use single quotes inside an alias,
3.1 close quote,
3.2 dq sq dq
3.3 open quote

> alias nut='net use |perl -lne '"'"'print("$1=$2") if s,\\,/,g && m@\s([A-Z]):\s+(//\S+)@'"'"
^^^^^ (close sq, open dq, insert sq, close dq, open sq again).

> alias vq='vim +'"'"' ":call Mosh_Quick_Fix()" '"'"' '
^^^^^ (close sq, open dq, insert sq, close dq, open sq again).
> alias vq
vq="vim +'\" :call Mosh_Quick_Fix()\" ' "

4. Use dq to preserve spaces:
> x='A
B'
> echo $x # single line, newlines in $x are converted to spaces.
A B
> echo "$x" # newlines \n are preserved.
A
B

5. Passing dq to emacs from a function:
ediff(){ ${EMACS:=emacs} --eval "(ediff-files \"$1\" \"$2\")" }

6. Passing arguments to perl
> echo PWDLEN=$(perl -e 'print length(shift);' $PWD)
> echo PWDLEN=$(perl -e 'print length($ARGV[0]);' $PWD)



Well you knew that? If not, get a copy of "Kernighan and Pike".
However Regexp are better done with expr and perl. ATT made
the perfect shell, but the implementation of regexp eluded them
until Henry spencer wrote the free bugfree regexp library.
Finally Larry Wall perfected the Camel (no relation of Caml).

Traditionally you can do plain literal subst OLD by NEW in VAR.

>VAR=${VAR/OLD/NEW}

You will have to come out of your shell to play with regexps:

subst(){
eval "VAL=\$$1"
VAL2=$(perl -e '($f,$a,$b)=(shift,shift,shift);$f=~s/$a/$b/gi;print $f' $VAL $2 $3)
eval "$1=\"$VAL2\""
}
>subst PATH '/usr/\w+/bin' '/usr/bin' # changes PATH =~ s,/usr/\w+/bin,/usr/bin,ig


Regexp can be eagerly quantified. see 'man perlre' for details.

4 Comments:

  • At 1:50 AM, Anonymous Kartik Agaram said…

    Puzzle. How would you modify this bash script:
    for i in `ls -d`; do echo $i; done

    to handle directories with spaces?

    :)

     
  • At 6:27 PM, Anonymous Anonymous said…

    IFS='';
    for i in * ;do echo $i ;done

    will preserve spaces in the glob!
    - Mohsin.

     
  • At 10:53 AM, Anonymous Kartik Agaram said…

    Close but not quite :) 'for i in *' is the easy case, you don't even need IFS for it. For iterating over a general list created using backticks you set IFS to newline right before the backtick - and set it back right after:
    IFS='
    '
    for i in `ls -d`; do IFS=' '; echo $i; done

    It's a good idea to minimize the time IFS is at a non-standard because it's such a fundamental variable.

     
  • At 3:37 PM, Anonymous Anonymous said…

    Quoting the backquotes protects spaces:

    for i in "`ls -1 c:/`" ;do echo "$i" ;done

    The spaces
    have given me the biggest headache on windows.
    - M

     

Post a Comment

<< Home