Bash – How to input n repetitions of a digit in bash, interactively

bashcommand historycommand lineline-editor

I'd like to run the command

foo --bar=baz <16 zeroes>

How do I type the 16 zeroes efficiently*? If I hold Alt and press 1 6 0 it will repeat the next thing 160 times, which is not what I want. In emacs I can either use Alt-[number] or Ctrl-u 1 6 Ctrl-u 0, but in bash Ctrl-u kills the currently-being-typed line and the next zero just adds a 0 to the line.

If I do

foo --bar=baz $(printf '0%.0s' {1..16})

Then history shows exactly the above, and not foo --bar=baz 0000000000000000; i.e. bash doesn't behave the way I want. (Edit: point being, I want to input some number of zeroes without using $(...) command substitution)

(*) I guess a technical definition of "efficiently" is "with O(log n) keystrokes", preferably a number of keystrokes equal to the number of digits in 16 (for all values of 16) plus perhaps a constant; the emacs example qualifies as efficient by this definition.

Best Answer

Try

echo Alt+1Alt+6Ctrl+V0

That's 6 key strokes (assuming a US/UK QWERTY keyboard at least) to insert those 16 zeros (you can hold Alt for both 1 and 6).

You could also use the standard vi mode (set -o vi) and type:

echo 0Escx16p

(also 6 key strokes).

The emacs mode equivalent and that could be used to repeat more than a single character (echo 0Ctrl+WAlt+1Alt+6Ctrl+Y) works in zsh, but not in bash.

All those will also work with zsh (and tcsh where that comes from). With zsh, you could also use padding variable expansion flags and expand them with Tab:

echo ${(l:16::0:)}Tab

(A lot more keystrokes obviously).

With bash, you can also have bash expand your $(printf '0%.0s' {1..16}) with Ctrl+Alt+E. Note though that it will expand everything (not globs though) on the line.

To play the game of the least number of key strokes, you could bind to some key a widget that expands <some-number>X to X repeated <some-number> times. And have <some-number> in base 36 to even further reduce it.

With zsh (bound to F8):

repeat-string() {
  REPLY=
  repeat $1 REPLY+=$2
}
expand-repeat() {
  emulate -L zsh
  set -o rematchpcre
  local match mbegin mend MATCH MBEGIN MEND REPLY
  if [[ $LBUFFER =~ '^(.*?)([[:alnum:]]+)(.)$' ]]; then
    repeat-string $((36#$match[2])) $match[3]
    LBUFFER=$match[1]$REPLY
  else
    return 1
  fi
}
zle -N expand-repeat
bindkey "$terminfo[kf8]" expand-repeat

Then, for 16 zeros, you type:

echo g0F8

(3 keystrokes) where g is 16 in base 36.

Now we can further reduce it to one key that inserts those 16 zeros, though that would be cheating. We could bind F2 to two 0s (or two $STRING, 0 by default), F3 to 3 0s, F1F6 to 16 0s... up to 19... possibilities are endless when you can define arbitrary widgets.

Maybe I should mention that if you press and hold the 0 key, you can insert as many zeros as you want with just one keystroke :-)

Related Question