Bash – Pass length of argument into bash command substitution

bashcommand-substitutionshell-script

I'm trying to write a quick bash function that populates a README.md with a $1\n followed by underscores the length of $1.

The code I found in other stackexchange questions showed that to print a character <n> times, use

printf '=%.0s' {1..<n>}

and indeed, this works (obviously replacing <n> with a number).

To create my README.md, I thought the function would look something like this:

make_readme() {
    echo "$1
$(printf '=%.0s' {1..${#1}})" > README.md
}

make_readme "Some project"

This, however, produces a file with this text:

Some project
=

As far as I can tell, ${#1} within the $(...) is being replaced with the empty string. My guess is that command substitutions get their own argument scopes, and since there are no arguments passed to the substitution, $1 is being replaced with nothing.

I did finally finagle a couple workarounds:

make_readme() {
    underline="printf '=%.0s' {1..${#1}}"
    echo "$1
$(eval "$underline")" > README.md
}

or

make_readme() {
    echo "$1" > README.md
    printf '=%.0s' {1..${#1}} >> README.md
}

but it seems like there should be a way to do this in one line.

Best Answer

Suggestion:

#!/bin/bash

make_readme () {
    printf '%s\n%s\n' "$1" "$( eval "printf '=%.0s' {1..${#1}}" )"
}

make_readme 'Hello World!' >README.md

or, if calling an external utility is ok,

#!/bin/bash

make_readme () {
    # print $1, then replace all characters in it with = and print again
    sed 'p; s/./=/g' <<<"$1"
}

make_readme 'Hello World!' >README.md

Both of these generate a file called README.md containing

Hello World!
============
Related Question