How do I print a horizontal line of characters like ----?
There are many different ways, depending on how fancy you'd like to be. This page is a restoration of content from the now-defunct bash-hackers wiki, with some additional text.
The simplest way: Just print it
printf '%s\n' --------------------
This does exactly what it looks like, no more, and no less. If you want a variable number of hyphens, continue reading.
Iterative ways
This one simply loops 20 times, always prints a hyphen, and adds a newline at the end. The number of loop iterations may be changed by passing a value in a variable:
for ((i = 0; i < 20; i++)); do printf %s - done echo
The printf command uses unbuffered I/O, so it actually writes a hyphen once per loop iteration. This may be unappealing (e.g. too slow) in some applications. If you prefer to print the entire line all at once, you can build it up in a variable first:
line= for ((i=0; i<20; i++)); do line+=- done printf '%s\n' "$line"
Implicit printf looping
This one uses the printf command to print an empty field with a minimum field width of 20 characters. The text is padded with spaces, but since there is no text, you get 20 spaces. The spaces are then converted to - by the tr command.
printf '%20s\n' | tr ' ' -
Without an external command, using the (non-POSIX) substitution expansion and -v option:
printf -v res %20s printf '%s\n' "${res// /-}"
Sizing it to the terminal
This is a variant of the above that uses the $COLUMNS variable if it's set, or tput cols otherwise, to find the width of the terminal and set that number as the minimum field witdh.
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
Fancy printf tricks
This one is a bit tricky. The format for the printf command is %.0s, which specifies a field with the maximum length of zero. After this field, printf is told to print a dash. You might remember that it's the nature of printf to repeat, if the number of conversion specifications is less than the number of given arguments. With brace expansion {1..20}, 20 arguments are given (you could easily write 1 2 3 4 … 20, of course!). Following happens: The zero-length field plus the dash is repeated 20 times. A zero length field is, naturally, invisible. What you see is the dash, repeated 20 times.
# Note: you might see that as ''%.s'', which is a (less documented) shorthand for ''%.0s'' printf '%.0s-' {1..20}; echo
This variant requires bash 4.2 or newer:
printf '%(-)T' {1..20}; echo
It (ab)uses the %(timefmt)T feature, passing a time format string with no date or time elements.
A major drawback of these examples is that it's difficult to pass a length parameter to them, because {1..$n} doesn't work. The standard workaround is to use eval:
n=20 eval "printf '%(-)T' {1..$n}"; echo
This works well, but you must ensure that the content of $n is a valid number. Likewise, for the %.0s- example, and if we want to size it to the terminal:
# Note: COLUMNS should be sanity-checked first. # Note: we call tput *outside* of the eval, but ensuring a safe PATH is still wise. c=${COLUMNS:-$(tput cols)} eval "printf %.0s- {1..$c}"; echo
Or restrict the length to 1 and prefix the arguments with the desired character.
# COLUMNS should be sanity-checked first. c=${COLUMNS:-$(tput cols)} eval "printf %.1s -{1..$c}"; echo
The Crazy ormaaj Way™
This one completely depends on Bash due to its brace expansion evaluation order and array parameter parsing details. As above, the eval only inserts the COLUMNS expansion into the expression and isn't involved in the rest, other than to put the _ value into the environment of the _[0] expansion. This works well since we're not creating one set of arguments and then editing or deleting them to create another as in the previous examples.
_=- command eval printf %s '"${_[0]"{0..'"${COLUMNS:-$(tput cols)}"'}"}"'; echo
Note: This example no longer works in bash 5.0 or higher.
Using parameter expansions
Preparing enough hyphens in advance, we can then use a non-POSIX subscript expansion:
hr=---------------------------------------------------------------\ ---------------------------------------------------------------- printf '%s\n' "${hr:0:${COLUMNS:-$(tput cols)}}"
The drawback to this is that the terminal may be wider than our variable. Here's a more flexible approach, and also using modal terminal line-drawing characters instead of hyphens:
This one builds up the line variable until it's at least large enough for the terminal, then prints a substring that's exactly the width of the terminal. The modal line-drawing characters may not work in all terminals.