简体   繁体   中英

Escape single quote inside double quote for executing a command in shell scripting

function install_rubygems {
    #install rubygems
    ruby_cmd=("sudo gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3"
     "bash -c 'curl -sSL https://get.rvm.io|bash -s stable'"
     "source /etc/profile.d/rvm.sh"
     #"rvm install 1.9.2 --with-zlib-directory=/usr/local/rvm/usr --with-openssl-directory=/usr/local/rvm/usr"
     #"rvm --default use 1.9.2"
     #"gem install soap4r-ruby1.9 log4r net-ldap json httpclient"
  )
for cmd in  "${ruby_cmd[@]}"
do
  $cmd
  exit_status=$?
  if [ "$exit_status" -ne "0" ]; then
     echo "Error occured while running: $cmd. Exiting..."
     exit
  fi
done
}

I want to execute commands listed in ruby_cmd array one by one and then check the exit status of each and perform some operation after that. But when I execute the above script in shell it gives the error as below:

-sSL: -c: line 0: unexpected EOF while looking for matching `'' -sSL: -c: line 1: syntax error: unexpected end of file

I am pretty new to shell scripting, could anyone please tell me what is the correct way to write above command? PS: command is correct and it executes properly if directly run on bash

For a general discussion of the topic, see BashFAQ #50 .

There are several approaches available:

Use a function

install_rubygems() {
  sudo gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 && \
    curl -sSL https://get.rvm.io | bash -s stable && \
    source /etc/profile.d/rvm.sh
)

install_rubygems || {
  retval=$?
  echo "Error occurred while running; exiting..." >&2
  exit "$retval"
}

Use an eval-able array

This is safe only if none of your strings can contain user-input. Otherwise, it opens you to shell injection attacks.

install_rubygems=(
  "sudo gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3"
  "curl -sSL https://get.rvm.io | bash -s stable"
  "source /etc/profile.d/rvm.sh"
)
for cmd in "${install_rubygems[@]}"; do
  eval "$cmd" || {
    retval=$?
    echo "Error occurred while running $cmd; exiting..." >&2
    exit "$retval"
  }
done

Use multiple single-command arrays

Because each array here has only a single simple command -- no pipelines, no indirections, no substitutions performed at evaluation time -- it's somewhat more secure than the eval approach: Only places where it's explicitly running code through expansion (like the argument to bash -c or to an explicit eval ) are prone to undefined behavior, so you could safely use untrusted data in other locations (such as the argument to --keyserver ) with confidence that the shell won't do anything untoward with it, even if that data is attempting a shell injection attack.

install_rubygems_01=( sudo gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 )
install_rubygems_02=( eval 'curl -sSL https://get.rvm.io | bash -s stable' )
install_rubygems_03=( source /etc/profile.d/rvm.sh )

for varname in "${!install_rubygems_@}"; do

  # there's a safer way to do this in bash 4.3, but that's not widely deployed yet
  # (this particular command shouldn't ever be unsafe unless varname has been tampered
  # with, but it had to be constructed very carefully).
  eval "cmd=( \"\${$varname[@]}\" )"

  ## ...specifically, in bash 4.3, you could do this instead of the above eval:
  #declare -n cmd=$varname

  # evaluate array as an exact argv
  "${cmd[@]}" || {
    retval=$?
    printf -v cmd_str '%q ' "${cmd[@]}"
    echo "Error occurred while running ${cmd_str% }; exiting..." >&2
    exit "$retval"
  }

  ##Using bash 4.3 namevars instead of the eval above, you'd want to do this here:
  #unset -n cmd
done

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM