将变量从交互式会话传递到Bash脚本。

huangapple go评论50阅读模式
英文:

Pass variables out of an interactive session from bash script

问题

你好,以下是你要的中文翻译:

大家好,世界的人们,

我正在尝试编写一个脚本,允许用户在bash中在站点之间执行应用程序故障转移。

我们的应用程序由Pacemaker控制,我认为我可以编写一个接受必要变量并执行操作的函数。在一个站点上停止,在另一个站点上启动。一旦我通过`ssh`连接到远程机器,我无法获取`grep`/`awk`命令的返回值,用于PCS应用程序的状态。

我遇到了一些问题,并尝试了来自Stack Overflow和其他网站的答案。
1) 我将`ssh`命令发送到`/dev/null 2>&1`,因为本地用户上的Unix管理员有屏幕上弹出的横幅,而`-q`不能处理它 - 这是否会阻止任何内容返回?
2) 在代码中使用`awk '{print \\\\\\$4}'`时,我收到了“反斜杠不是行上的最后一个字符”错误。
3) 为了解决这个问题,我尝试了`result=$(sudo pcs status | grep nds_$resource)`,然而这导致了`sudo`密码错误。
4) 我尝试了`>/dev/tty和>$(tty)`
5) 我尝试不压制`ssh`(删除`/dev/null 2>&1`),并将输出放入函数调用的变量中,从`sudo pcs status`条目中移除`awk`。

这是可以的,但当我添加`| awk '{print \\\$4}'`时,我得到了横幅中的第四个单词。

任何帮助将不胜感激,因为我已经在做这个几天了。

我一直在研究[Bruno的这个答案](https://stackoverflow.com/questions/2559076/how-do-i-redirect-output-to-a-variable-in-shell/15170225#15170225),但不确定如何实施,因为我有多个`sudo`命令。

以下是我在一个机器上进行测试的函数代码的简化版本:

site1=lon
site2=ire

function pcs_call()
{
 site=$1
 serverA=$2
 serverB=$3
 activity=$4
 resource=$5

ssh -tt ${site}servername0${serverA} <<SSH &gt; /dev/null 2&gt;&amp;1
         sudo pcs resource ${activity} proc_${resource}
         sleep 10
         sudo pcs status  | grep proc_$resource | awk '{print \\\$4}' | tee $output
         exit
SSH
echo $output
}

echo ====================================================================================
echo 在$site1中关闭PMR
pcs_call "$site1" "1" "2" "disable" "pmr"
英文:

Hello People of the world,

I am trying to write a script that will allow user to failover apps between sites in bash.

Our applications are controlled by Pacemaker and I thought I would be able to write a function that would take in the necessary variables and act. Stop on one site, start on another. Once I have ssh'd to the remote machine, I am unable to get the value of the grep/awk command back for the status of the application in PCS.

I am encountering a few issues, and have tried answers from stackoverflow and other sites.

  1. I send the ssh command to /dev/null 2&gt;&amp;1 as banners pop up on screen that unix admin have on the local user and -q does not deal with it - Does this stop anything being returned?
  2. when using awk &#39;{print \\\\\\$4}&#39; in the code, I get a "backslash not last character on line" error
  3. To get round this, I tried result=$(sudo pcs status | grep nds_$resource), however this resulted in a password error on sudo
  4. I have tried &gt;/dev/tty and &gt;$(tty)
  5. I tried to not suppress the ssh (remove /dev/null 2&gt;&amp;1) and put the output in variable at function call, removing the awk from the sudo pcs status entry.
result=$(pcs_call &quot;$site1&quot; &quot;1&quot; &quot;2&quot; &quot;disable&quot; &quot;pmr&quot;)
echo $result | grep systemd 

This was OK, but when I added | awk &#39;{print \\\$4}&#39; I then got the fourth word in the banner.

Any help would be appreciated as I have been going at this for a few days now.

I have been looking at this answer from Bruno, but unsure how to implement as I have multiple sudo commands.

Below is my strip down of the function code for testing on one machine;

site1=lon
site2=ire


function pcs_call()
{
 site=$1
 serverA=$2
 serverB=$3
 activity=$4
 resource=$5

ssh -tt ${site}servername0${serverA}  &lt;&lt;SSH &gt; /dev/null 2&gt;&amp;1
         sudo pcs resource ${activity} proc_${resource}
         sleep 10
         sudo pcs status  | grep proc_$resource | awk &#39;{print \\\$4}&#39; | tee $output
         exit
SSH
echo $output
}

echo ====================================================================================
echo Shutting Down PMR in $site1
pcs_call &quot;$site1&quot; &quot;1&quot; &quot;2&quot; &quot;disable&quot; &quot;pmr&quot;

答案1

得分: 0

  1. &gt; /dev/null 表示“将返回的任何数据丢弃到bitbucket。2&gt;&amp;1 表示“将stderr上的任何有用错误报告发送到stdout正在传输的地方”。你最初的语句旨在从远程系统检索信息,但立即将其丢弃。除非你只是希望远程系统上发生一些你不希望在本地更多了解的事情,否则在此之后的任何事情都是浪费时间,因为你已经丢弃了它的内容。

  2. awk语句中,只需要一个反斜杠来引用$4中的美元符号。

  3. 除非你在远程系统上具有无密码的sudo权限,否则这不会为你起作用。在深入讨论之前,我认为我们需要更多关于这方面的信息。

  4. 只要ssh调用将所有内容传输到/dev/null,传递的代码块内部不会在调用系统上提供任何结果。

  5. 在你的代码中,你正在使用$output,但看起来你的意图是让tee来设置它?这不是它的工作方式。tee的参数是一个文件名,它期望将数据的副本写入其中,同时也将其流式传输到stdout(就像管道中的“T”型接头在管道中将流体分为两部分),但它不会分配变量。

在最后,你执行了echo $output,这个变量可能为空,所以它基本上只是echo,不会发送除换行符之外的任何内容,这将被发送回原始服务器并丢弃到/dev/null,所以这都没有什么意义。

让我们清理一下:

sudo pcs status | grep proc_$resource | awk '{print \$4}' | tee $output

然后尝试一种稍微不同的方法,对吗?

首先,我将假定你具有无密码的sudo权限,否则需要进一步讨论。

其次,通常在管道中同时使用grepawk是一个反模式,因为它们在本质上都是正则表达式引擎。选择其中一个。如果你可以让grep实现你想要的效果,它是相当高效的。如果不能,awk非常灵活。当某些东西无法正常工作时,请阅读你正在使用的工具的文档页面是非常重要的。快速搜索“bash man grep”或“awk手册”将快速为你提供出色的资源,如果你尝试进行这种复杂的操作,你会需要它们。

因此,让我们看一下重新设计,做一些假设:

function pcs_call() {
  local site="$1" serverA="$2" activity="$3" resource="$4" # 将变量声明为本地变量并使用引号
  ssh -qt ${site}servername0${serverA} "
    sudo pcs resource ${activity} proc_${resource}; sleep 10; sudo pcs status; 
  " 2>&1 | awk -v resource="$resource" '$0~"proc_"resource { print $4 }'
}

pcs_call "$site1" 1 disable pmr # 应该打印所需字段

如果你想捕获数据到一个变量以供以后使用:

var1="$( pcs_call "$site1" 1 disable pmr )"

附加说明

回答你的问题 - 使用$(seq 1 10)或者{1..10}

ssh -qt chis03 '
    for i in {1..10}; do sudo pcs resource disable ipa $i; done;
    sleep 10; sudo pcs status;
' 2>&1 | awk -v resource=ipa '$0~"proc_"resource { print $2" "$4 }'

awk首先报告是因为管道中元素的顺序是“未定义的”,但ssh的stdout被插入到awk的stdin(并且由于它也被复制到stdout,所以stderr也是如此),所以它们是异步/同时运行的。

是的,因为这些是使用字面值,单引号更简单,实际上“更好”。如果使用变量进行抽象,它并不会改变太多,但切换回双引号。

# 假设我的变量(svr、verb、target)在上下文中预设
ssh -qt $svr "
    for i in {1..10}; do sudo pcs resource $verb $target $i; done;
    sleep 10; sudo pcs status;
" 2>&1 | awk -v resource="$target" '$0~"proc_"resource { print $2" "$4 }'

这对你有帮助吗?

英文:

I'd say start by pasting the whole thing into ShellCheck.net and fixing errors until there are no suggestions, but there are some serious issues here shellcheck is not going to be able to handle alone.

  1. &gt; /dev/null says "throw away into the bitbucket any data that is returned. 2&gt;&amp;1 says "Send any useful error reporting on stderr wherever stdout is going". Your initial statement, intended to retrieve information from a remote system, is immediately discarding it. Unless you just want something to occur on the remote system that you don't want to know more about locally, you're wasting your time with anything after that, because you've dumped whatever it had to say.
  2. You only need one backslash in that awk statement to quote the dollar sign on $4.
  3. Unless you have passwordless sudo on the remote system, this is not going to work out for you. I think we need more info on that before we discuss it any deeper.
  4. As long as the ssh call is throwing everything to /dev/null, nothing inside the block of code being passed is going to give you any results on the calling system.
  5. In your code you are using $output, but it looks as if you intend for tee to be setting it? That's not how that works. tee's argument is a filename into which it expects to write a copy of the data, which it also streams to stdout (tee as in a "T"-joint, in plumbing) but it does NOT assign variables.

(As an aside, you aren't even using serverB yet, but you can add that back in when you get past the current issues.)

At the end you echo $output, which is probably empty, so it's basically just echo which won't send anything but a newline, which would just be sent back to the origin server and dumped in /dev/null, so it's all kind of pointless....

Let's clean up

sudo pcs status | grep proc_$resource | awk &#39;{print \\\$4}&#39; | tee $output

and try it a little differently, yes?

First, I'm going to assume you have passwordless sudo, otherwise there's a whole other conversation to work that out.

Second, it's generally an antipattern to use both grep AND awk in a pipeline, as they are both basically regex engines at heart. Choose one. If you can make grep do what you want, it's pretty efficient. If not, awk is super flexible. Please read the documentation pages on the tools you are using when something isn't working. A quick search for "bash man grep" or "awk manual" will quickly give you great resources, and you're going to want them if you're trying to do things this complex.

So, let's look at a rework, making some assumptions...

function pcs_call() {
  local site=&quot;$1&quot; serverA=&quot;$2&quot; activity=&quot;$3&quot; resource=&quot;$4&quot; # make local and quotes habits you only break on purpose
  ssh -qt ${site}servername0${serverA} &quot;
    sudo pcs resource ${activity} proc_${resource}; sleep 10; sudo pcs status; 
  &quot; 2&gt;&amp;1 | awk -v resource=&quot;$resource&quot; &#39;$0~&quot;proc_&quot;resource { print $4 }&#39;
}

pcs_call &quot;$site1&quot; 1 disable pmr # should print the desired field

If you want to cath the data in a variable to use later -

var1=&quot;$( pcs_call &quot;$site1&quot; 1 disable pmr )&quot;

addendum

Addressing your question - use $(seq 1 10) or just {1..10}.

ssh -qt chis03 &#39;
    for i in {1..10}; do sudo pcs resource disable ipa $i; done;
    sleep 10; sudo pcs status;
&#39; 2&gt;&amp;1 | awk -v resource=ipa &#39;$0~&quot;proc_&quot;resource { print $2&quot; &quot;$4 }&#39;

It's reporting the awk first, because order of elements in a pipeline is "undefined", but the stdout of the ssh is plugged into the stdin of the awk (and since it was duped to stdout, so is the stderr), so they are running asynchronously/simultaneously.

Yes, since these are using literals, single quotes is simpler and effectively "better". If abstracting with vars, it doesn't change much, but switch back to double quotes.

# assuming my vars (svr, verb, target) preset in the context
ssh -qt $svr &quot;
    for i in {1..10}; do sudo pcs resource $verb $target $i; done;
    sleep 10; sudo pcs status;
&quot; 2&gt;&amp;1 | awk -v resource=&quot;$target&quot; &#39;$0~&quot;proc_&quot;resource { print $2&quot; &quot;$4 }&#39;

Does that help?

huangapple
  • 本文由 发表于 2023年2月8日 20:33:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75385852.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定