6

The second line of the script only works if I trigger glob expansion by executing echo. I can't understand why. Here's the command and it's execution to give some context.

Function definition:

~/ cat ~/.zsh/includes/ascii2gif
ascii2gif () {
        setopt extendedglob
        input=$(echo ${1}(:a))
        _path=${input:h}
        input_f=${input:t}
        output_f=${${input_f}:r}.gif
        cd $_path
        nerdctl run --rm -v $_path:/data asciinema/asciicast2gif -s 2 -t solarized-dark $input_f $output_f
}

Activate function debugging for ascii2gif function..

~/ typeset -f -t ascii2gif

Debugged function execution:

~/ ascii2gif ./demo.cast
+ascii2gif:1> input=+ascii2gif:1> echo /Users/b/demo.cast
+ascii2gif:1> input=/Users/b/demo.cast
+ascii2gif:2> _path=/Users/b
+ascii2gif:3> input_f=demo.cast
+ascii2gif:4> output_f=demo.gif
+ascii2gif:5> cd /Users/b
+_direnv_hook:1> trap -- '' SIGINT
+_direnv_hook:2> /Users/b/homebrew/bin//direnv export zsh
+_direnv_hook:2> eval ''
+_direnv_hook:3> trap - SIGINT
+ascii2gif:6> nerdctl run --rm -v /Users/b:/data asciinema/asciicast2gif -s 2 -t solarized-dark demo.cast demo.gif
==> Loading demo.cast...
==> Spawning PhantomJS renderer...
==> Generating frame screenshots...
==> Combining 40 screenshots into GIF file...
==> Done.

I've tried numerous variations to try and force expansion such as input=${~1}(:a) etc, but to no avail. Any suggestions? Obviously the script works but seems sub-optimal.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
bryan hunt
  • 63
  • 3
  • The 2nd line `ascii2gif () {`? – ctrl-alt-delor Mar 30 '22 at 13:20
  • And the first line `~/ cat ~/.zsh/includes/ascii2gif` does? – ctrl-alt-delor Mar 30 '22 at 13:21
  • Unrelated: `setopt` and variable assignments have a global effect by default. When you change options for the purposes of a function, either use `setopt local_options` or, usually, use `emulate -L zsh` so that your function will work if some options you weren't thinking about have been changed from the default. Also make variables local with `local` (or `typeset`). Also it's probably a bad idea to change the current directory in this function. Or you could run the whole function in a subshell by putting `(…)` around the body instead of `{…}` (but still keep `emulate zsh`). – Gilles 'SO- stop being evil' Mar 30 '22 at 20:55

2 Answers2

5

That's because the way you're trying to use the a modifier here is for globbing, and no globbing takes place in var=WORD (because globbing, in general, can result in multiple words, so it doesn't take place in contexts that expect a single word). So you're relying on globbing taking place inside the command substitution, and then assigning the result to the variable.

Since the a modifier can be used on parameter expansion but with a different way of applying it, you can try that:

input=${1:a}

For example:

% cd /tmp
% foo() { input=${1:a}; typeset -p input; }
% foo some-file
typeset -g input=/tmp/some-file
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
muru
  • 69,900
  • 13
  • 192
  • 292
3

/u muru's answer looks like the right way to go about this, but the reason why it's not expanding is that filename generation (globbing) does not take place in scalar assignments (it does in array assignments, because globs - in general - may return multiple matches):

From man zshoptions:

   GLOB_ASSIGN <C>
          If this option is set, filename generation  (globbing)  is  per‐
          formed on the right hand side of scalar parameter assignments of
          the form `name=pattern (e.g. `foo=*').  If the result  has  more
          than  one  word  the  parameter  will become an array with those
          words as arguments. This option is provided for  backwards  com‐
          patibility  only: globbing is always performed on the right hand
          side of array  assignments  of  the  form  `name=(value)'  (e.g.
          `foo=(*)')  and  this form is recommended for clarity; with this
          option set, it is not possible to  predict  whether  the  result
          will be an array or a scalar.

So

$ zsh -c 'f=${1}(:a); echo $f' zsh file.txt
file.txt(:a)

but

$ zsh -c 'f=(${1}(:a)); echo $f' zsh file.txt
/home/steeldriver/dir/file.txt
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
steeldriver
  • 78,509
  • 12
  • 109
  • 152