10

A bash script is using a variable Q for some purpose (outside the scope of this question):

Q=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

As this script is used in an environment where each byte counts, this is waste. But some workaround like

Q=0$(seq -s "" 9)$(echo {A..Z}|tr -d " ")

(for C locale) is even worse. Am I too blind to see the obvious trick to compactly generate such a simple sequence?

Philippos
  • 13,453
  • 30
    https://codegolf.stackexchange.com/ ... – ilkkachu Sep 21 '21 at 10:25
  • 21
    That's all of 39 bytes, including the newline. Is that really a problem? On a system that's capable of running a POSIX-compatible or POSIX-like shell?? – ilkkachu Sep 21 '21 at 10:26
  • 1
    @ilkkachu The script can be part of packets transferred over a poor network. – Philippos Sep 21 '21 at 10:53
  • 2
    ok, fair. Would it be possible to preload that snippet on the systems that need it? Then you could do just . a to load it. Or even better, set it as an environment variable somewhere where it then becomes available to the script. – ilkkachu Sep 21 '21 at 11:06
  • @ilkkachu That would be preferred, if I could control that system. – Philippos Sep 21 '21 at 11:11
  • ah, indeed. My condolences. – ilkkachu Sep 21 '21 at 11:13
  • @ilkkachu My own fault. I can release an update, but those packets need to be compatible with legacy systems, probably forever. I should have thought of this before the first release, but Q was introduced later. sigh – Philippos Sep 21 '21 at 11:20
  • 4
    If you have some standard tools available on the receiving end, maybe you could compress the script before sending it, un-compressing it on the other end before running? It is possible to create a bash script that decompresses itself when run; for a short script the overhead involved wouldn't pay off, but if every byte counts there is some number of bytes where it may be worth it, if possible. – spuck Sep 21 '21 at 14:38
  • 5
    This is definitely in the domain of micro-optimisations. Before starting to address things like this, I hope you've spent a whole lot of time trying to get rid of every larger-scale structural inefficiencies (which may include things like, if applicable, compressing it before sending it, sending it less often or making network or low-level message configuration changes). – NotThatGuy Sep 22 '21 at 00:01
  • 1
    What character encoding is in use? Because you've written {A..Z}, I'm guessing it's one in which the letters are contiguous, but the question would be better if you made that explicit. – Toby Speight Sep 22 '21 at 06:50
  • 1
    Sometimes the reason for wishing such compactness is not about saving system resources. It could be a case of something that must go across an air gap (has to be read and typed in by hand), or for recovery of a system where only console access without copy paste is possible, or ... exploit code for whatever-hat purposes. – rackandboneman Sep 22 '21 at 18:26

4 Answers4

26

For any shell capable of brace expansion:

Using printf:

$ printf %s {0..9} {A..Z}
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

-->

Q=$(printf %s {0..9} {A..Z})

Backticks instead of $() saves one byte. For Bash specifically, printf -v var to printf into a variable is nice but no shorter than backticks.

printf -vQ %s {0..9} {A..Z}
Q=`printf %s {0..9} {A..Z}`
Peter Cordes
  • 6,466
pLumo
  • 22,565
  • 9
    printf can print to variables directly without a subshell by using the -v option: printf -v Q %s {0..9} {A..Z} – spuck Sep 21 '21 at 14:30
  • 4
    Use backticks to save 1 byte. – annahri Sep 21 '21 at 22:39
  • 4
    @annahri That's bad advice. – l0b0 Sep 22 '21 at 00:53
  • 11
    @l0b0: Not really. It's not like backticks are going away; there certainly are downsides to backticks (e.g. less readable, some restrictions on complex commands) - but for this specific case, in an environment where you're trying to save every byte, it's a perfectly reasonable tradeoff. – psmears Sep 22 '21 at 11:05
  • @annahri you can use perl and save another 3 bytes: Q=\perl -Esay+0..9,A..Z`` . Just kidding. The idea that you would "save" anything by replacing a simple string with a command substitution (forking another process, setting up its i/o, and all the heavy lifting that it entails) it totally ludicrous. I guess that the only point of this "question" was to gather views and rep points. –  Sep 23 '21 at 07:16
  • @UncleBilly I strongly believe that this question is for code golfing. – annahri Sep 28 '21 at 23:40
  • @annahri Eventually, this answer did make it into a real world system. No hobby golfing. – Philippos Mar 17 '22 at 16:32
8

The simplest way I know is to use bash way:

echo {0..9} {A..Z}|sed 's/ //g'

(sed is used to remove space between symbols. Space between brackets is important otherwise you will see all combination from 0 to 9 and A to Z like 0A0B0C0D0E0F0G0H0I0J0K0L0M0N0O0P0Q0R0S0T0U0V0W0X0Y0Z1A1B1C...)

By suggest you can shorter the command like this:

echo {0..9} {A..Z}|tr -d ' '
Romeo Ninov
  • 17,484
  • 1
    Thank you. It works with my busybox. My tr -d " " is a little shorter than your sed 's/ //g', so I can change to Q=$(echo {0..9} {A..Z}|tr -d " "). Your answer is really a progress, but no match for the printf solution. – Philippos Sep 21 '21 at 11:14
  • 1
    @ilkkachu BusyBox v1.30.1, but – oops! – I just see that ash symlinks to busybox, but sh symlinks to bash.bash, which is a bash 4.4.23. So excuses: My question had a mistake. – Philippos Sep 21 '21 at 11:29
  • 1
    @RomeoNinov, that's pretty much part of what I was saying. If Solaris has (had) as sh something they call "Bourne Shell", but with csh or ksh features, it just makes it harder to know what people actually mean when they say "Bourne Shell". (On top of all the people who just call Bash or POSIX sh, or POSIX-like shells "Bourne", without specifying it any further, that is.) – ilkkachu Sep 21 '21 at 12:18
  • 8
    If you're playing golf, replace tr -d " " with tr -d \ (one trailing space) and save a character at the expense of readability – Chris Davies Sep 21 '21 at 15:44
  • 1
    For the record, even with @roaima's suggestion, Q=`echo {0..9} {A..Z}|tr -d \ ` is 4 bytes longer than an optimal printf capture or -vQ. I tried but tr won't accept the char to delete as part of the same argument, so tr -d\ doesn't work. – Peter Cordes Sep 22 '21 at 21:07
4

Frame challenge: don't.

Any way you could do this will be at best more resource-intensive than just writing what you already wrote, and the more efficient ways are also less portable (forcing you to write a "Bash script" rather than a "shell script").

In general, regardless of language, the optimal form for constant tables is a literal constant table, not something generated programmatically at runtime. If it's large enough that hard-coding it would be error-prone, generate it programmatically while writing the program or during "build time" for the program, and include the output as a literal constant in your actual program.

-4

Again, don't, but if you really must, a pattern that generalizes decently without needing special shell features is:

printf %b $(printf \\x%.2x $(seq 48 57) $(seq 65 90))

The seq utility can be written as a shell function if you don't want to depend on it.