8

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?

11
  • 30
    codegolf.stackexchange.com ...
    – ilkkachu
    Sep 21 at 10:25
  • 20
    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 at 10:26
  • 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 at 11:06
  • 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 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 at 0:01
25

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}`
6
  • 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 at 14:30
  • 4
    Use backticks to save 1 byte.
    – annahri
    Sep 21 at 22:39
  • 3
    @annahri That's bad advice.
    – l0b0
    Sep 22 at 0: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 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 at 7:16
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 ' '
5
  • 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 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 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 at 12:18
  • 7
    If you're playing golf, replace tr -d " " with tr -d \ (one trailing space) and save a character at the expense of readability
    – roaima
    Sep 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. Sep 22 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.

6
  • 1
    Writing stuff like that manually can be more error prone (ie, missing or repeating a letter).
    – Shadow
    Sep 22 at 0:33
  • @Shadow: My answer addresses that. "If it's large enough that hard-coding it would be error-prone..." Sep 22 at 2:32
  • 1
    It doesn't have to be large for that to be a possible concern.
    – Shadow
    Sep 22 at 4:13
  • 1
    @Shadow - putting the literal into the script doesn't mean that it needs to be typed manually. You could use the technique in other answers to generate it and then paste it in Sep 22 at 18:01
  • 1
    @MartinSmith: Which is exactly what I advocated in the third paragraph of this answer. :-) Sep 22 at 19:13
-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.

4
  • How was this nominated for deletion?? Sep 22 at 12:27
  • It's not a shell feature - it's a separate program. If you don't want to depend on it, it can be written as a shell function. Sep 22 at 15:28
  • True, I made a mistake there. My point is: You mention portability and resource intensiveness in your other answer, but Seq is not part of POSIX either and this answers spawns a couple of processes. I don't see advantage in it.
    – Quasímodo
    Sep 22 at 17:45
  • 1
    The value of the answer is the pattern for generating strings from sequences of numeric character codes, which doesn't inherently involve seq just printf. Sep 22 at 17:52

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.