在shell脚本中执行命令的双引号内单引号

时间:2016-06-03 13:58:16

标签: linux 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
}

我想逐个执行ruby_cmd数组中列出的命令,然后检查每个命令的退出状态,然后执行一些操作。但是当我在shell中执行上面的脚本时,它会给出如下错误:

-sSL:-c:第0行:在寻找匹配的'''时出现意外的EOF -sSL:-c:第1行:语法错误:意外的文件结尾

我是shell脚本的新手,有人可以告诉我上面命令的正确写法是什么? PS:命令是正确的,如果直接在bash上运行,它会正确执行

1 个答案:

答案 0 :(得分:4)

有关该主题的一般性讨论,请参阅BashFAQ #50

有几种方法可供选择:

使用功能

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"
}

使用可评估数组

如果您的字符串都不能包含用户输入,那么这是安全的 。否则,它会打开shell注入攻击。

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

使用多个单命令数组

因为这里的每个数组只有一个简单的命令 - 没有管道,没有间接,没有在评估时执行替换 - 它比eval方法更安全:只有它显式运行代码的地方扩展(如bash -c或显式eval的参数)容易出现未定义的行为,因此您可以安全地在其他位置使用不受信任的数据(例如--keyserver的参数)相信shell不会做任何与之相关的事情,即使这些数据正在尝试shell注入攻击。

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