Search code examples
bashincrementmultilinemultilinestring

How to increment an integer while passing a multi-line string to a command using "Here tag" in bash


I have a piece of code that looks similar to this:

index=SOME_INTEGER
SOME_COMMAND <<EOF
subcommand1
${index} subsubcommand1
subcommand2
$((${index}+1)) subsubcommand2
subcommand3
$((${index}+2)) subsubcommand3
subcommand4
$((${index}+3)) subsubcommand4
subcommand5
$((${index}+4)) subsubcommand5
EOF

This method of manually adding one more to ${index} for each new subcommand is quite tedious, and not very practical if i need to rearrange the order/delete lines (the subcommand1 line must precede the subsubcommand1 line and the indexes must be given from lowest to highest), so i was wandering if there was a simple way to increment ${index} between <<EOF and EOF without referencing an exact number, or if there was an alternative way to pass the subcommands to SOME_COMMAND that would allow for this.

I have tried to give various commands like the following but they have not worked:

$((index=$((index+1))))
$((index=index+1))
$((index+=1))
$((index++))

Solution

  • First: If you switch from a postincrement to a preincrement, your original code works. That is:

    SOME_COMMAND() { cat; }
     
    index=5
     
    SOME_COMMAND <<EOF
    subcommand1
    ${index} subsubcommand1
    subcommand2
    $((++index)) subsubcommand2
    subcommand3
    $((++index)) subsubcommand3
    subcommand4
    $((++index)) subsubcommand4
    subcommand5
    $((++index)) subsubcommand5
    EOF
    

    properly emits as output:

    subcommand1
    5 subsubcommand1
    subcommand2
    6 subsubcommand2
    subcommand3
    7 subsubcommand3
    subcommand4
    8 subsubcommand4
    subcommand5
    9 subsubcommand5
    

    If that didn't work, though, the easy, surefire thing to do here would be to write a function that generates your desired input. You can then either expand that function into a heredoc or herestring (if SOME_COMMAND needs its input to be seekable), or use it directly as a process substitution (if SOME_COMMAND only needs its input to be streamable).

    input_for_SOME_COMMAND() {
      local index mainCommand
      index=$1; shift || return
      for arg; do
        printf '%s\n' "$arg" "$((index++)) sub$arg"
      done
    }
    
    SOME_COMMAND <<<"$(input_for_SOME_COMMAND 1 subcommand{1,2,3,4,5})"
    

    See this working in an online sandbox at https://ideone.com/XOUauQ