Используйте $(...)
вместо обратного синтаксиса для подстановки внешней команды. Таким образом:
I='foo.png'
var1=$(cat <<EOL
![](../images/${I%.*}.png)
\`\`\`sql
some code here
\`\`\`
EOL
)
echo "$var1"
Посмотрите, как работает и испускает желаемый результат, на https://ideone.com/nbOrIu
В противном случае вам нужно больше обратные косые черты:
I='foo.png'
var1=`cat <<EOL
![](../images/${I%.*}.png)
\\\`\\\`\\\`sql
some code here
\\\`\\\`\\\`
EOL
`
echo "$var1"
... и если бы вы вложили обратные черты в обратные косые черты, вам нужно было бы еще раз умножить свои обратные косые черты. Просто скажите «Нет» подстановке команд на основе обратного ключа.
Кстати, кое-что, что вы могли бы решить, чтобы вообще избежать этой проблемы:
I='foo.png'
fence='```'
var1=$(cat <<EOL
![](../images/${I%.*}.png)
${fence}sql
some code here
${fence}
EOL
)
echo "$var1"
... помещение буквальных обратных символов в переменная означает, что вам больше не нужен какой-либо вид экранирования, чтобы не рассматривать их как синтаксис.