20

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.

  • 1
    Sounds like you want bash to just somehow know what you want to do. In a complex nested command how would bash know which parts you wanted to see the result of the execution in the history as opposed to the command itself? What about variables? In short bash is always going to have the code in the history, not the result of executing the code. – Unbeliever Oct 10 '16 at 13:59
  • @meuh, Alt by itself doesn't send any character, so pressing and releasing alt won't have any effect as far as bash is concerned. – Stéphane Chazelas Oct 10 '16 at 14:05
  • 1
    @Unbeliever Do What I Mean and it'll be Okay – cat Oct 10 '16 at 22:50
  • @Unbeliever: see my edit—the point of the paragraph having to do with history was that I would like to input digits without using command substitution. – Jonas Kölker Oct 11 '16 at 7:55
  • Whenever you want things typed automatically for you, AutoKey is a great utility to use. With it, you can build phrase substitutions, or complete Python macros that will do almost anything you like when a trigger phrase is typed or a hotkey is pressed. I didn't include this as an answer because it requires a GUI desktop environment and the rest of the answers do not - and are thus more generally applicable. – Joe Oct 15 '16 at 3:50
25

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 :-)

  • Thanks for providing multiple variations for vi and others! – Mark Stewart Oct 10 '16 at 23:10
9

Presuming your terminal emulator doesn't eat the keystroke (e.g., to switch tabs)—plain xterm works—you can press Alt-1 6 Ctrl-V 0. The control-V is needed in there to break up the number and the character to repeat (if you were repeating a letter, you wouldn't need it).

(This is a readline feature known as digit-argument, so you should be able to use bind to change the modifier keys involved)

  • 1
    Thank you, including the name of the feature is useful when people like me stumble on these questions. Related links for a bit more reading: SO Question and bash manual – admalledd Oct 10 '16 at 20:10
4

In Emacs, I'd normally use C-q to separate prefix argument from digit input.

Bash uses Ctrl+v instead of C-q, to avoid problems with XON/XOFF flow control.

So you can use Alt+1 Alt+6 Ctrl+v 0.

3

another option is to type four zeroes and then use the mouse to copy and paste it three more times. double-click to select, middle-click x 3 to paste.

  • Or golf it / cheat some more by typing one zero, select & paste that to get 2; select & paste that to get 4; select & paste to 8, and again for 16. It's POWerful :) – Jeff Schaller Oct 10 '16 at 15:30
  • yeah, but typing 4 zeroes and repeat-pasting them seems the optimal level of effort vs payoff to me. all that extra copying and pasting seems like work. – cas Oct 11 '16 at 8:56
3

Playing with macros:

Bind the function key F8 to multiply by two the last word (up the previous space) (F8 key code found by using Ctrl-V F8):

$ bind '"\e[19~": "\C-w\C-y\C-y"'

That could be made permanent by sending the same text to ~/.inputrc

$ echo '"\e[19~": "\C-w\C-y\C-y"' >> ~/.inputrc

then type:

echo 0F8F8F8F8

to get 2^4 times the zero. (still 5 keystrokes).

or type:

echo bookF8F8F8

to get 2^3 book words.

Still faster:

Multiply by 4:

$ bind '"\e[19~": "\C-w\C-y\C-y\C-w\C-y\C-y"'

echo 0F8F8

3 keypress.

Multiply by 8 (the same number as the function key)

$ bind '"\e[19~": "\C-w\C-y\C-y\C-w\C-y\C-y\C-w\C-y\C-y"'

echo 00F8

Still 3 keypresses.

Cheat?

Cheat by multipling by 16.

$ bind '"\e[19~": "\C-w\C-y\C-y\C-w\C-y\C-y\C-w\C-y\C-y\C-w\C-y\C-y"'

echo 0F8

Only 2 keypress. (and still an useful simple function)

^^^^^^^^^^^^^^^^ (base 36? Hah!) :-P

Plainly cheating:

$ bind '"\e[19~": "0000000000000000"'

echo F8

Just 1 (yes: one) keystroke.



Changing the binding for ctrl+U:

Send this to ~/.inputrc:

echo '"\C-u": universal-argument >> ~/.inputrc

Re-read the ~/.inputrc file:

ctrl+Xctrl+R

do it as is usual in emacs (as you wanted):

foo --bar=baz ctrl+U 16 ctrl+U 0

7 keys (after the "setting up").

Slightly shorter:

Use the default "multiply by 4" of "universal-argument" and end with

 ctrl+V 0 

foo --bar=baz ctrl+Uctrl+Uctrl+V0

Only 5 keys.

Using the alt+n access to (arg: n)

 foo --bar=baz Alt+16Ctrl+V0

That's 6 keys to get the 16 zeros.

Not changing any keyboard shortcut:

If in your bash you have bash C-u kills the currently-being-typed line.
That's because "\C-u": is bind to unix-line-discard.

But that might also help:
When, what is before the cursor is erased, it is also placed in the "kill-ring".

So ctrl+u erases and ctrl+y yanks back what was erased.
On a clean line: Type 00 erase it and yank it back twice to make it 0000.
Repeat to get 00000000 (8 zeros), finally type the command and yank back twice.

The first set (7 keys keep ctrl pressed):

00 ctrl+Uctrl+Yctrl+Y ctrl+U 

The second set (5 keys)

ctrl+Uctrl+Yctrl+Y ctrl+U 

That will get eight zeros in the erase ring, then type what you want:

 foo --bar=baz ctrl-Y ctrl-Y 

to get:

foo --bar=baz 0000000000000000

After you get the idea, you may as well, type what you need, go to the start of the line (ctrl-Y), do as above (up to eight zeros) go to the end (ctrl-E) and yank twice.

foo --bar=baz ctrl-A00 ctrl-Uctrl-Yctrl-Y ctrl-Uctrl-Yctrl-Y ctrl-U ctrl-Ectrl-Yctrl-Y

That's 15 keys (beside the command itself).
Not short, I know, but that is working only with what was available.

This is a little shorter:

0000 ctrl-U ctrl-Y ctrl-Y ctrl-Y ctrl-Y ctrl-A foo --bar=baz

That's 11 keys

  • See edit of my answer ;-) – Stéphane Chazelas Oct 11 '16 at 10:14
  • @StéphaneChazelas Using macros? Base 36? Only in zsh? C'mon ... see my edit :-P – sorontar Oct 11 '16 at 11:18
  • Well yes, the base36 was a bit tongue in cheek. I like your F<n> duplicating the last word <n> times. – Stéphane Chazelas Oct 11 '16 at 11:35
  • Note that not all terminal send the same sequence upon F8 (though most of those found on GNU/Linux systems do send ^[[19~). See "$(tput kf8)" to get the information from the terminfo database (or the $terminfo hash in zsh). – Stéphane Chazelas Oct 11 '16 at 11:51
  • @StéphaneChazelas Yes, that's correct. Or simply use ctrl-v F8 – sorontar Oct 11 '16 at 12:08
2

As a variation of cas’s answer, type

printf '0%.s' {1..16}Enter
and then use the mouse to copy and paste it (once).  Arguably, this is O(log n), but since it is (len(str(n))+20), you might consider it inefficient.  But note:

  • I was able to shave off one character — on my system, %.s seems to behave the same as %.0s.
  • If you want to run multiple commands with 0000000000000000 as an argument, you need to do this only once.

glenn jackman points out that printf "%0*d\n" number 0 (where number is a positive integer) will print a string of number zeros (and a newline), as the asterisk in the format tells printf to take the field width from the arguments.  Specifically, printf "%0*d" 16 0 will print 16 zeros.  But this is a special case that handles only 0printf "%0*d" 16 12345 will print 0000000000012345, and printf "%0*d" 16 foo will print 0000000000000000 and an error message.  By contrast, the other commands in this answer will produce strings like 123451234512345… or foofoofoo… (and see below for 7%7%7%...).

If you’re going to do this on a frequent basis, you can streamline it by defining a shell function:

rep() { printf -- "$1%.0s" $(seq 1 "$2"); printf "\n"; }

Notes:

  • Use $(seq 1 "$2") instead of (1.."$2"} because the latter won’t work — see this, this, this, and this.  Note that some of the answers to those questions suggest using eval, but this is generally not recommended.
  • The format string ($1%.0s) must be in double quotes (rather than single quotes) because it contains a parameter ($1).
  • The -- is to protect against $1 values beginning with -.
  • You could get similar protection by using "%.0s$1" instead of "$1%.0s".
  • Either way, a $1 value containing % will cause it to choke.
  • If you need to be able to handle $1 values containing %, do

    rep() { local i; for ((i=0; i<$2; i++)); do printf '%s' "$1"; done; printf "\n"; }
    

Also, once you have such a function defined, you can do things like

foo --bar=baz "$(rep 0 16)"

which is slightly less typing than "$(printf '0%.0s' {1..16})".

  • Alternately printf "%0*d" 16 0 will print 16 zeros. The asterisk in the format will take the field width from the arguments – glenn jackman Oct 11 '16 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.