2017-05-16 17:29:13 +0100, Stephane Chazelas:
[...]
> >   | Here, I'd fire awk and quote more than one arg at a time:
> > 
> > Hmm - you're really aiming for maximum sluggishness...  I could beat that
> > by just adding a couple of sleeps ...
> 
> Depends. If quoting only a handful a arguments, then that call
> to awk might cost you you a couple of milliseconds indeed. But
> if processing thousands, you might find that it saves a few
> seconds.

Actually, even for a handful of arguments, and even with gawk,
it seems it's generally quicker to use awk in my tests:

With 5 arguments (where "a" uses your quote() and "b" uses mine, see below):

(zsh syntax below)

$ szsh() (exec -a sh zsh "$@")
$ for shell (dash bash mksh szsh ksh93 yash) for file (a b) (TIMEFMT="$shell 
$file %*E"; time (repeat 100 $shell ./$file "a'b'c"{1..5}) > /dev/null)
dash a 0.329
dash b 0.407
bash a 0.942
bash b 0.528
mksh a 1.598
mksh b 0.540
szsh a 0.763
szsh b 0.622
ksh93 a 0.667
ksh93 b 0.464
yash a 0.738
yash b 0.429

In mksh, printf is not built-in which doesn't help. In all but
ksh93, that still does 5 forks because of the $(set +o).

dash is the only one that manages to be quicker (not if I use
mawk instead of gawk though).

For 3000 arguments, that's where we see the real advantage of using a real
programming language instead of inadequate features of a shell :-b:

$ for shell (dash bash mksh szsh ksh93 yash) for file (a b) (TIMEFMT="$shell 
$file %*E"; time ($shell ./$file "a'b'c"{1..3000}) > /dev/null)
dash a 0.827
dash b 0.019
bash a 7.712
bash b 0.080
mksh a 9.928
mksh b 0.022
szsh a 2.274
szsh b 0.028
ksh93 a 1.184
ksh93 b 0.022
yash a 2.655
yash b 0.035



the scripts under test:



$ cat a
quote() {
        case "$1" in
        *\'*)   ;;               # the harder case, we will get to below.
        *)      printf "'%s'" "$1"; return 0;;
        esac

        _save_IFS="${IFS}"     # if possible just make IFS "local"
        _save_OPTS="$(set +o)"  # quotes there not really needed.
        IFS=\'
        set -f
        set -- $1
        _result_="${1}"        # we know at least $1 and $2 exist, as there
        shift                  # was one quote in the input.

        for __arg__
        do
                _result_="${_result_}'\\''${__arg__}"
        done
        printf "'%s'" "${_result_}"

        # now clean up

        IFS="${_save_IFS}"              #none of this is needed with a good 
"local"...
        eval "${_save_OPTS}"
        unset __arg__ _result_ _save_IFS _save_OPTS

        return 0;
}
s=$(
  for i do
    quote "$i"; printf ' '
  done
)
printf '%s\n' "$s"

$ cat b
quote() {
  LC_ALL=C awk -v q="'" -v b='\\' '
    function quote(s) {
      gsub(q, q b q q, s)
      return q s q
    }
    BEGIN {
      sep = ""
      for (i = 1; i < ARGC; i++) {
        printf "%s", sep quote(ARGV[i])
        sep = " "
      }
      if (sep) print ""
    }' "$@"
}
s=$(quote "$@")
printf '%s\n' "$s"

-- 
Stephane

Reply via email to