【问题标题】:Function calls in Here Document for unix shell script用于 unix shell 脚本的 Here Document 中的函数调用
【发布时间】:2022-01-07 04:53:54
【问题描述】:

我正在编写一个 shell 脚本来移动一些文件。该脚本需要能够根据传入的参数对本地计算机或远程服务器上的文件进行操作。我已经设法组合了一个简单的函数来满足我的需求。我似乎无法弄清楚如何在 Here 文档中使用该功能,以便它可以在远程服务器上执行。我在这里发现了一个类似的问题:From shell script can we invoke function from here document 但给定的答案对我不起作用。

这是我到目前为止的想法:

myscript.sh

REMOTE_HOST=myserver
JETTY_BASE=/opt/web/jetty

doMove()
{
    echo "$JETTY_BASE/webapps/eyerep-data/$1"       
    sudo touch $JETTY_BASE/webapps/eyerep-data/$1/myfile
    ls $JETTY_BASE/webapps/eyerep-data/$1;
}

moveRemote()
{
    echo "attempting move with here doc"    
    ssh -t $REMOTE_HOST "/bin/bash <<EOF
$(doMove $1) 
EOF"
    
}

moveFiles()
{   
        case "$1" in
      # remote deploy
      remote)
        moveRemote $2
        ;;
      # local deploy
      local)
        doMove $2
        ;;
      *)
        echo "Usage: myscript.sh {local|remote}"
        exit 1
        ;;
    esac
    
}

如果我运行上面的

./myscript.sh 远程开发

我得到如下输出:

attempting move with here doc
/bin/bash: line 1: /opt/web/jetty/webapps/eyerep-data/: Is a directory
/bin/bash: line 2: dev: command not found
/bin/bash: line 3: eyerep-data-dev.xml: command not found
/bin/bash: line 4: eyerep-data-local.xml: command not found
/bin/bash: line 5: local: can only be used in a function

查看输出,它似乎试图将来自“echo”和“ls”调用的输出作为命令传递给 /bin/bash,而不是将它们打印到控制台。虽然这是一个人为的例子,但我希望能够在我的函数中包含打印到标准输出的日志语句。处理这个问题的最佳方法是什么?

【问题讨论】:

  • $(doMove $1) 在函数体中产生执行命令的结果;它不会为远程 shell 生成这些命令的列表来执行。
  • 那么正确的语法是什么?
  • 一般来说没有。 doMove是本地定义的,不是远程定义的,所以不能在远程主机上调用。
  • 使用说明不是错误消息。您应该发出错误消息并退出 1,或者您应该发出用法语句并退出 0。如果您选择编写错误消息,请将其打印到 stderr。

标签: bash shell unix ssh


【解决方案1】:

试试这个:

#!/bin/bash

REMOTE_HOST=myserver
JETTY_BASE=/opt/web/jetty

generateMoveCommands() {
    __="echo ${JETTY_BASE@Q}/webapps/eyerep-data/${1@Q}
sudo touch ${JETTY_BASE@Q}/webapps/eyerep-data/${1@Q}/myfile
ls ${JETTY_BASE@Q}/webapps/eyerep-data/${1@Q}"
}

moveLocal() {
    generateMoveCommands "$1"
    eval "$__"
}

moveRemote() {
    echo "Attempting move with here doc"
    generateMoveCommands "$1"
    ssh -t "$REMOTE_HOST" "$__"
}

showUsage() {
    echo "Usage: $0 {local|remote} file"
    exit 1
}

main() {
    case $1 in
    # remote deploy
    remote)
        [[ $2 ]] || showUsage
        moveRemote "$2"
        ;;
    # local deploy
    local)
        [[ $2 ]] || showUsage
        moveLocal "$2"
        ;;
    *)
        showUsage
        ;;
    esac
}

main "$@"

${param@Q} 形式将 value 扩展为引用版本,允许安全地重新评估作为参数。这是从 4.​​4 开始提供的 Bash 功能。

替代解决方案:

#!/bin/bash

REMOTE_HOST=myserver
JETTY_BASE=/opt/web/jetty

doMove() {
    local jetty_base=$1 file=$2
    echo "${jetty_base}/webapps/eyerep-data/${file}"
    sudo touch "${jetty_base}/webapps/eyerep-data/${file}/myfile"
    ls "${jetty_base}/webapps/eyerep-data/${file}"
}

moveRemote() {
    echo "Attempting move with here doc"
    ssh -t "$REMOTE_HOST" "$(declare -fp doMove)"$'\n'"doMove ${JETTY_BASE@Q} ${1@Q}"
}

showUsage() {
    echo "Usage: $0 {local|remote} file"
    exit 1
}

main() {
    case $1 in
    # remote deploy
    remote)
        [[ $2 ]] || showUsage
        moveRemote "$2"
        ;;
    # local deploy
    local)
        [[ $2 ]] || showUsage
        doMove "$JETTY_BASE" "$2"
        ;;
    *)
        showUsage
        ;;
    esac
}

main "$@"

【讨论】:

  • generateMoveCommands 中的 '__' 的用途是什么?这是特殊的语法还是可以用普通的变量名代替?
  • 它就像任何其他变量一样。我将它用作临时的全局和通用迭代器。
  • 到目前为止没有运气。如果我尝试运行该脚本,当它尝试进行 sudo 调用时,我最终会得到“sudo:没有 tty 存在并且没有指定 askpass 程序”。
  • @pbuchheit 我的错误。我基于您的原始命令。您不应指定/bin/bash-c。只需立即指定命令,即:ssh -t "$REMOTE_HOST" "$__"。但是,此解决方案要求 /bin/shbash,或者您只能在其中使用 POSIX 兼容的命令。
  • 该解决方案足以解决原始问题。我还有一些其他问题,但它们超出了原始问题的范围,所以我在这里创建了一个新问题:stackoverflow.com/questions/70614226/…。感谢您提供的所有帮助。
猜你喜欢
  • 2014-03-26
  • 1970-01-01
  • 2014-10-11
  • 2012-10-26
  • 2016-06-22
  • 2018-02-13
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多