8

To create a column header, looking like:

1234567890123456789

I (am trying to) use seq and echo:

seq -s '' 1 9 ; echo -n 0; seq -s '' 1 9

However, seq outputs a newline after each run. How can I avoid that?

poige
  • 6,231
GreenMatt
  • 725
  • would you care to elaborate why you can't just echo 1234567890123456789? and why you need just 19 columns? and why the echo -n 0; seq -s '' 1 9 instead of seq -s '' 0 9? –  Jan 05 '19 at 18:42
  • @pizdelect: Q1 & 2: I'm looking for an expandable method that might be put in a scripts; Q3: Because I don't want a leading 0. – GreenMatt Jan 09 '19 at 19:39
  • Q1 & Q2. what do you want to expand it to? I explained in my answer how to make a header as wide as the terminal, without writing the numbers byte by byte. And that's easy to adapt to eg. a header like abcdeabcde.... Q3. the two commands from my comment are equivalent. –  Jan 09 '19 at 20:10
  • If any of the answers solved your problem, please accept it by clicking the checkmark next to it. Thank you! – Jeff Schaller Jan 13 '19 at 15:15

7 Answers7

11

Assuming you just want to print 1234567890123456789, you can do it with:

$ printf "%s" $(seq 1 9) $(seq 0 9)
1234567890123456789$

That won't have a trailing newline at all though, so maybe you prefer:

$ printf "%s" $(seq 1 9) $(seq 0 9) $'\n'
1234567890123456789
$

A few simpler choices if you don't need to use seq:

$ perl -le 'print 1..9,0,1..9'
1234567890123456789
$ printf "%s" {1..9} {0..9} $'\n'
1234567890123456789

Since you mentioned portability, I recommend you use the perl approach, or if you are likely to encounter systems without perl, and yet need the same command to run in shells including bash, sh, dash, tcsh etc, try Kamil's approach.

terdon
  • 242,166
  • Thanks. How portable is that? It worked under Bash. Under tcsh I got an "Illegal variable name" error. Under sh, the '\n' was displayed. – GreenMatt Jan 03 '19 at 20:17
  • 1
    @GreenMatt ah, that depends. For extreme portability, you may as well just use the perl one which is shell-agnostic. The printf "%s" $(seq 1 9) $(seq 0 9) will work in any POSIX shell, including sh and dash, but the printf "%s" $(seq 1 9) $(seq 0 9) $'\n' will fail in dash. Note that it does work in sh (or at least in bash running as sh, so in POSIX mode), I assume you're running Ubuntu or a simmilar system whose sh is actually dash. tcsh is a strange one and not even trying to be POSIX so don't expect any of the shell-based approaches to work there. – terdon Jan 03 '19 at 20:21
  • 3
    @GreenMatt of course, if you need portability, you should also avoid using echo. – terdon Jan 03 '19 at 20:25
  • 2
    @GreenMatt lol. Say what you like about Perl, you just can't beat it for text manipulation and it is extremely portable. – terdon Jan 03 '19 at 21:19
  • To be tcsh compatible: printf "%s\n" "\\seq 1 9; seq 0 9`"` – Larry Jan 03 '19 at 21:30
  • printf "%b" $(seq 1 9) $(seq 0 9) '\n' should work in dash (%b is POSIX if I didn't misread) – ilkkachu Jan 03 '19 at 21:50
  • @ilkkachu it's the ansi escape ($'\n') that doesn't work, and the '\n' just prints a literal \n, I think. I'm on mobile now so I can't check though. – terdon Jan 03 '19 at 21:55
  • @terdon printf %b processes the escapes within printf, so $'..' isn't needed. printf %s of course doesn't do that. – ilkkachu Jan 03 '19 at 21:56
  • @GreenMatt, tcsh isn't a POSIX-family shell, so it can't be reasonably expected to be compatible with... well, anything else that isn't in the (small, and deeply frowned-upon) csh family. – Charles Duffy Jan 03 '19 at 21:59
  • There are 2 things that don't work in the example with tcsh: 1. the $() form of command substitution, 2. the ANSI string expansion. @ilkkachu: "%b" does expand substituted newlines, but you can also include it in the format string itself. – Larry Jan 03 '19 at 22:19
  • 1
    @Larry, not with that printf structure you can't, the format string gets repeated for each argument, but we only want one newline. – ilkkachu Jan 03 '19 at 22:22
  • @ilkkachu: Yes, but you can have both seq commands in a single argument, like in my example. Except for the backslash before the first seq, which is just stupid, I left it in somehow. – Larry Jan 03 '19 at 22:27
  • 1
    @Larry, mm, the command you posted as a comment prints one digit per line. But I'm starting to think you should post that as an answer of your own if you want to refine or discuss it further. – ilkkachu Jan 03 '19 at 22:35
  • @ilkkachu You are right. My bad, I actually tested the "%b" variant and didn't know literal newlines will be interpreted differently between "%s" and "%b". "%b" it is. – Larry Jan 03 '19 at 22:40
  • perl -le 'print 1..9,0,1..9' is nice - single process, instead of two as in printf and seq in command substitution – Sergiy Kolodyazhnyy Jan 03 '19 at 23:31
6

You can remove newlines from any stream with tr -d '\n'. In your case

(seq -s '' 1 9 ; echo -n 0; seq -s '' 1 9) | tr -d '\n'

While other answers may concentrate on modifying your original approach, this is the way that should just remove newline characters regardless of what is before |.

  • 1
    Nice. This also has the benefit of being portable to most (all?) shells. I think the OP wants the final newline, so another option might be ( seq -s '' 1 9 ; echo -n 0 ; seq -s '' 1 9 ) | tr -d '\n' ; printf "\n". – terdon Jan 03 '19 at 20:34
  • Ah yes, I never think of tr. – GreenMatt Jan 03 '19 at 20:38
  • +1. Also, worth saying that BSD version of seq does treat -s '' more fully than GNU's — no newline is printed even when it exits (they q-n wouldn't arise at all there) – poige Jan 04 '19 at 02:43
  • My take on this: https://unix.stackexchange.com/a/492380/6622 – poige Jan 04 '19 at 04:09
5

I assume you don't want just that particular string, but strings of incrementing digits with variable lengths. For that, it may be useful to be able to change the width by changing one number, instead of having to build it from multiple calls to seq.

In Bash, you could use something like this (for a 19-long sequence):

for ((i=1; i <= 19; i++)); do printf "%d" "$(( i % 10 ))"; done; echo

This should work in a standard shell (and works in at least dash and busybox, anyway):

i=1; while [ "$i" -le 19 ]; do printf "%d" "$(( i % 10 ))"; i=$((i+1)); done; echo
ilkkachu
  • 138,973
  • +1: The latter approach looks like everything it needs should be guaranteed to be available in any POSIX-y shell to me. – Charles Duffy Jan 03 '19 at 22:01
4

Consider also:

printf '%d' $(seq -w 1 1 99 | cut -c2)

We generate the numbers 01..99, zero-padded (-w), then strip off the tens-place. Those ones-place numbers are then sent to printf to be printed individually.

To get your desired output, use 19 instead:

$ printf '%d' $(seq -w 1 1 19 | cut -c2)
1234567890123456789
JoL
  • 4,735
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
1

When using tr to get rid of trailing new-line it's worth realising that it deletes all occurrences of the characters given in its -d option. Thus you can get it a bit more slim:

(seq 1 9; echo 0; seq 1 9) | tr -d '\n'

— even echo doesn't need to have -n anymore.

And for completeness sake: BSD's version of seq when -s '' specified doesn't produce new-line on its exit.

poige
  • 6,231
1

To remove the trailing newline, just use a command execution:

 $ echo "test$(seq -s '' 9)no-newline"
 test12345679no-newline

Or capture it in a variable:

 $ var=$(seq -s '' 9); echo "${var}0${var}"
 1234567890123456789

Or also:

 $ printf '%s' {{0..9},0,{0..9}}; echo
 1234567890123456789
0

I wonder why you don't just:

echo 1234567890123456789

And what's the point of printing just 19 columns instead of 20?

If you want it repeated (by default: for the whole width of the terminal)

chdr(){ c=${1:-$COLUMNS}; while [ "$c" -gt 0 ]; do printf '%.*s' "$c" 1234567890; c=$((c-10)); done; echo; }
chdr 17
12345678901234567
chdr
12345678901234567890123456789012345678901234567890123456789012345678901234567890

Of course, that won't work in csh (huh).

FYI: seq isn't portable (there's jot on *BSD, but it's quite different).