-2

I am using here document to create a startup script, when I checked my code on https://www.shellcheck.net it gives me a warning which it can be ignored. But I am wondering if there is a better way to write a code and avoid that warning if possible.

https://github.com/koalaman/shellcheck/wiki/SC2129

cat << EOF >> /usr/local/etc/rc.d/"${name}"
#!/bin/sh
. /etc/rc.subr
cpu="${cpu}"
ram="${ram}"
tap="${tap}"
name="${name}"
EOF
  sed -n '3,16p' ./bhyve >>  /usr/local/etc/rc.d/"${name}"
  cat << EOF >> /usr/local/etc/rc.d/"${name}"
start_cmd=${name}_start
stop_cmd=${name}_stop
restart_cmd=${name}_restart
delete_cmd=${name}_delete
${name}_start() {
EOF
  cat << 'EOF' >> /usr/local/etc/rc.d/"${name}"
  while true; do
    bhyve -Hw -c "${cpu}" -m "${ram}"G \
      -s 0,hostbridge \
      -s 1,virtio-net,"${tap}" \
      -s 2,virtio-blk,/dev/zvol/zroot/VMs/"${name}"/disk0 \
      -s 29,fbuf,tcp=0.0.0.0:"${vnc}",w=1024,h=768 \
      -s 30,xhci,tablet \
      -s 31,lpc \
      -l bootrom,/zroot/VMs/efi.fd \
      "${name}" || break;
  done > /dev/null 2>&1 &
}
EOF
  cat << EOF >> /usr/local/etc/rc.d/"${name}"
${name}_stop() {
EOF
  cat << 'EOF' >> /usr/local/etc/rc.d/"${name}"
  bhyvectl --vm="${name}" --force-poweroff
}
EOF
  cat << EOF >> /usr/local/etc/rc.d/"${name}"
${name}_restart() {
EOF
cat << 'EOF' >> /usr/local/etc/rc.d/"${name}"
  bhyvectl --vm="${name}" --force-reset
}
EOF
  cat << EOF >> /usr/local/etc/rc.d/"${name}"
${name}_delete() {
EOF
  cat << 'EOF' >> /usr/local/etc/rc.d/"${name}"
  bhyvectl --vm="${name}" --destroy
  sleep 5
  ifconfig "${tap}" destroy
  sysrc cloned_interfaces-="${tap}"
EOF
  cat << EOF >> /usr/local/etc/rc.d/"${name}"
  sed -i '' 's/ addm ${tap}//g' /etc/rc.conf
  sed -i '' 's/service ${name} start || sleep 5//g' /usr/local/etc/rc.d/bhyve
  sed -i '' '/^$/d' /usr/local/etc/rc.d/bhyve
EOF
  cat << 'EOF' >> /usr/local/etc/rc.d/"${name}"
  zfs destroy -r zroot/VMs/"${name}"
  rm /usr/local/etc/rc.d/"${name}"
}

load_rc_config "${name}"
run_rc_command "$1"
EOF
  # Add the VM to /usr/local/etc/rc.d/bhyve for autostart
  sed -i '' -e "9s/^/service ${name} start || sleep 5\n/g"  /usr/local/etc/rc.d/bhyve
7
  • @Quasimodo Hello, I've included the the warning thanks in advance. For the shebang it's on the right place since I am creating a new startup script. – Amr Dec 28 '20 at 0:01
  • 3
    The link you added clearly shows how it can be improved. What is your question?` – Panki Dec 28 '20 at 0:02
  • 1
    Thank you for the edit, but you should copy and paste the warning into the question itself. Questions should be as self-contained as possible. The shebang is still on the second line. – Quasímodo Dec 28 '20 at 0:03
  • 2
    github.com/koalaman/shellcheck/wiki/SC2129 contains the exact steps. What do you need more? – Panki Dec 28 '20 at 0:06
  • 2
    The problem with that then, is with a variable such as $name. In some instances it's going to be evaluated in the context of this script-writing-script and in other places it's going to be left for evaluation when your generated script runs. This surely can't be right – roaima Dec 28 '20 at 0:15
1

OK, I'll bite. First doing exactly what the site tells you, you get

{
cat << EOF
#!/bin/sh
. /etc/rc.subr
cpu="${cpu}"
ram="${ram}"
tap="${tap}"
name="${name}"
EOF
  sed -n '3,16p' ./bhyve
  cat << EOF
start_cmd=${name}_start
stop_cmd=${name}_stop
restart_cmd=${name}_restart
delete_cmd=${name}_delete
${name}_start() {
EOF
  cat << 'EOF'
  while true; do
    bhyve -Hw -c "${cpu}" -m "${ram}"G \
      -s 0,hostbridge \
      -s 1,virtio-net,"${tap}" \
      -s 2,virtio-blk,/dev/zvol/zroot/VMs/"${name}"/disk0 \
      -s 29,fbuf,tcp=0.0.0.0:"${vnc}",w=1024,h=768 \
      -s 30,xhci,tablet \
      -s 31,lpc \
      -l bootrom,/zroot/VMs/efi.fd \
      "${name}" || break;
  done > /dev/null 2>&1 &
}
EOF
  cat << EOF
${name}_stop() {
EOF
  cat << 'EOF'
  bhyvectl --vm="${name}" --force-poweroff
}
EOF
  cat << EOF
${name}_restart() {
EOF
cat << 'EOF'
  bhyvectl --vm="${name}" --force-reset
}
EOF
  cat << EOF
${name}_delete() {
EOF
  cat << 'EOF'
  bhyvectl --vm="${name}" --destroy
  sleep 5
  ifconfig "${tap}" destroy
  sysrc cloned_interfaces-="${tap}"
EOF
  cat << EOF
  sed -i '' 's/ addm ${tap}//g' /etc/rc.conf
  sed -i '' 's/service ${name} start || sleep 5//g' /usr/local/etc/rc.d/bhyve
  sed -i '' '/^$/d' /usr/local/etc/rc.d/bhyve
EOF
  cat << 'EOF'
  zfs destroy -r zroot/VMs/"${name}"
  rm /usr/local/etc/rc.d/"${name}"
}

load_rc_config "${name}"
run_rc_command "$1"
EOF
} > /usr/local/etc/rc.d/"${name}"

  # Add the VM to /usr/local/etc/rc.d/bhyve for autostart
  sed -i '' -e "9s/^/service ${name} start || sleep 5\n/g" /usr/local/etc/rc.d/bhyve

As @roaima suggests, at this point you get very very suspicious about the lines where you have ${name} inside 'EOF' blocks. You get away with it because you have a name="${name}" at the start of your generated file. At this point you change your script to something like this, where you change the ${name}_start and other function names into the actual names to make the program syntactically correct but leave other variables unexpanded until run time.

# Start of redirection to rc.d file       
{                                            
# Header, with variables being expanded.
cat << CAT_EOF                                                                             
#!/bin/bash                                                                                  
. /etc/rc.subr                               
cpu="${cpu}"       
ram="${ram}"                                 
tap="${tap}"                                 
name="${name}"                               
CAT_EOF                                                                                    
                                                                                           
# some stuff you want to preserve, probably would be better to
# have some markers in the file rather than magic line numbers                    
sed -n '3,16p' ./bhyve 

# Main body, changing ${name} to the name and leaving the rest intact.
# Maybe might want to add to the variables you want to expand in the future
sed 's/${name}/'"${name}"'/g;s/^  //' << 'SED_EOF'
  start_cmd=${name}_start
  stop_cmd=${name}_stop
  restart_cmd=${name}_restart
  delete_cmd=${name}_delete
  ${name}_start() {
    while true; do
      bhyve -Hw -c "${cpu}" -m "${ram}"G \
        -s 0,hostbridge \
        -s 1,virtio-net,"${tap}" \
        -s 2,virtio-blk,/dev/zvol/zroot/VMs/"${name}"/disk0 \
   /usr/local/etc/rc.d/"${name}"     -s 29,fbuf,tcp=0.0.0.0:"${vnc}",w=1024,h=768 \
        -s 30,xhci,tablet \
        -s 31,lpc \
        -l bootrom,/zroot/VMs/efi.fd \
        "${name}" || break;
    done > /dev/null 2>&1 &
  }
  ${name}_stop() {
    bhyvectl --vm="${name}" --force-poweroff
  }
  ${name}_restart() {
    bhyvectl --vm="${name}" --force-reset
  }
  ${name}_delete() {
    bhyvectl --vm="${name}" --destroy
    sleep 5
    ifconfig "${tap}" destroy
    sysrc cloned_interfaces-="${tap}"
    sed -i '' "s/ addm ${tap}//g" /etc/rc.conf
    sed -i '' "s/service ${name} start || sleep 5//g" /usr/local/etc/rc.d/bhyve
    sed -i '' '/^$/d' /usr/local/etc/rc.d/bhyve
    zfs destroy -r zroot/VMs/"${name}"
    rm /usr/local/etc/rc.d/"${name}"
  }
  
  load_rc_config "${name}"
  run_rc_command "$1"
SED_EOF
} > /usr/local/etc/rc.d/"${name}"


# Add the VM to /usr/local/etc/rc.d/bhyve for autostart
sed -i '' -e "9s/^/service ${name} start || sleep 5\n/g" /usr/local/etc/rc.d/bhyve
1
  • Thanks a lot for your answer, it was very helpful. – Amr Dec 28 '20 at 13:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.