想要通过将输出重定向到变量来检查命令是不是成功

Posted

技术标签:

【中文标题】想要通过将输出重定向到变量来检查命令是不是成功【英文标题】:Want to check whether a command succeeded by redirecting its output to a variable想要通过将输出重定向到变量来检查命令是否成功 【发布时间】:2011-08-07 18:47:18 【问题描述】:

我目前正在编写一个 bash 脚本,它使用 GoogleCL 将视频文件加载到 YouTube。

由于我正在循环上传内容(因为可能有多个视频文件),我想在上传下一个文件之前检查每个文件是否已成功上传。

命令google youtube post --access unlisted --category Tech $f(其中$f代表文件)输出一个字符串,告诉我上传是否成功。

但我不知道如何将“返回字符串”重定向到变量中以检查是否成功。

这就是我所拥有的:

for f in ./*.ogv ./*.mov ./*.mp4
do
    if [[ '*' != $f:2:1 ]]
    then
        echo "Uploading video file $f"

        # How to put the return value of the following command into a variable?
        google youtube post --access unlisted --category Tech $f > /dev/null

        # Now I assume that the output of the command above is available in the variable RETURNVALUE
        if [[ $RETURNVALUE == *uploaded* ]]
        then
            echo "Upload successful."
        else
            echo "Upload failed."
        fi
    fi
done

谁能帮帮我?

【问题讨论】:

【参考方案1】:

问题的答案是使用read 命令。

当然,所有这些其他答案都显示了不做 OP 所要求的方法,但这确实把我们其他搜索 OP 问题的人搞砸了。

免责声明:This is a duplicate answer

这是你的做法......

命令:

# I would usually do this on one line, but for readability...
series | of | commands \
| \
(
  read string;
  mystic_command --opt "$string" /path/to/file
) \
| \
handle_mystified_file

这是它正在做什么以及为什么它很重要:

    让我们假设series | of | commands 是一系列非常复杂的管道命令。 mystic_command 可以接受 stdin 作为文件,但 不是选项 arg 因此它必须作为变量进入** read 获取标准输入并将其放入变量 $string 不需要通过括号将readmystic_command 放入“子shell”,但可以使其像连续管道一样流动,就像两个命令在单独的脚本文件中一样。

** 总是有替代方案,在这种情况下,替代方案是丑陋且不可读的:

mystic_command --opt "$(series | of | commands)" /path/to/file | handle_mystified_file

【讨论】:

【参考方案2】:

访问 youtube 的示例

rm ~/temp/youtube_top_title1.txt rm ~/temp/youtube_top1.txt 卷曲“http://www.youtube.com/”|\ grep -o '观看[^"]*'|\ 排序-n|\ 唯一的-c|\ awk 'if ($1!="1") print $2'|\ 读我的时候;做\ page_youtube=`curl -s "http://www.youtube.com/$i"`;\ echo `echo -e "$page_youtube" |awk 'if ($1 ~ "") start=1; if ($1 ~ "") start=0; if (start == 1) print $0 '|grep -v ''` >> ~/temp/youtube_top_title1.txt url_current=$(echo -e "$page_youtube"|\ grep -o "url=[^,]*,"|\ sed 's/url=//g;s/,//g'|\ php -r '$f=fopen("php://stdin","r");while (FALSE!==($line=fgets($f))) echo urldecode($line);'|\ awk '打印 $1'|\ sed 's/;$//g'|\ sed 's/\\u.*//g'|\ 尾-2|\ 头 -1) echo "$url_current" >> ~/temp/youtube_top1.txt 完毕

【讨论】:

您可能会考虑在此答案中添加一些解释或有用的 cmets,以使其对发帖者更有用。【参考方案3】:

使用$()

variable=$(google youtube post --access unlisted --category Tech $f )

【讨论】:

因为 stdout 被重定向了,所以你不会捕获任何东西。 @glenn。是的,这是明目张胆的复制粘贴。【参考方案4】:

我的猜测是,您也可以依赖 google 命令中的错误代码(我假设如果上传失败,它会返回错误,但您可能应该仔细检查一下)。

for f in ./*.ogv ./*.mov ./*.mp4; do
  if [[ '*' != $f:2:1 ]]; then

    echo "Uploading video file $f"
    if google youtube post --access unlisted --category Tech "$f" > /dev/null
    then
      echo "Upload successful."
    else
      echo "Upload failed."
    fi

  fi
done

一个常见的误解是,如果要计算括号中的表达式,这是不正确的,如果总是接受一个命令并检查错误状态;通常这个命令是[,它是test 的别名,它计算表达式。 (是的,如果没有优化的快捷方式让它在 bash 中运行得更快,我会感到惊讶,但从概念上讲它仍然是正确的。

捕获输出是通过反引号完成的,就像这样

result=`command argument a b c`

或使用$()

result=$(command argument a b c)

但在这种情况下使用错误代码可能会更好。

编辑: 如果你的函数中有一个有趣的东西。我一开始没有注意到,但如果你启用nullglob shell 选项(这将使./*.mov 扩展为空字符串,如果没有文件)。另外,请引用$f,否则如果您的文件名包含空格,它将中断

shopt -s nullglob
for f in ./*.ogv ./*.mov ./*.mp4; do
  echo "Uploading video file $f"
  if google youtube post --access unlisted --category Tech "$f" > /dev/null
  then
    echo "Upload successful."
  else
    echo "Upload failed."
  fi
done

HTH。

【讨论】:

+1,我相信可以使用$? 作为if 中使用的错误代码,因为我们有一个相当大的命令。 @Nick,是的,你可以,然后在 if 上运行测试命令。不过它有点容易出错,因为中间的回声会毁掉它;所以你通常会想跳过几个圈来确定,比如google ..; result=$?; if [ $result -eq 0 ]; then ... @Nick;另请注意,如果您将“then”放在单独的行上(我认为这是一种风格问题),您只需在初始命令行if google... 中添加三个字符,然后在单独的行中添加then。我要把它编辑成那个。 谢谢你的精彩解释!谷歌代码确实返回了一个错误代码——我以前不知道,但我只是试过了,它工作正常。再次感谢! 请注意,间距很重要....变量名和= 之间不能有空格。无法弄清楚为什么它一直说我的变量是无效命令!【参考方案5】:

我会称之为The command ... outputs a string。 'Return' 是关键字,返回值是一个数字,其中 0 表示约定成功(0 个错误),不同的值表示错误代码。

您通过以下方式获取输出:

result=$(google youtube post --access unlisted --category Tech $f)

但经常会看到劣质的解决方案:

result=`cmd param1 param2`

劣质,因为反引号很容易与撇号混淆(取决于字体)并且难以嵌套,所以不要使用它们。

来自'man bash':

简单命令的返回值 是它的退出状态,或者 128+n 如果 命令由信号 n 终止。

和:

返回 [n] 使函数以指定的返回值退出 由 n。如果 n 被省略,则返回 状态是最后一个 在函数体中执行的命令。如果在外面使用 功能,但在执行期间 脚本由 . (资源) 命令,它会导致 shell 停止执行该脚本 并返回 n 或退出状态 最后一个命令的 在脚本中作为退出状态执行 脚本。如果在函数外使用 而不是在执行 . 编写的脚本,返回状态为假。任何命令 与 RETURN 陷阱相关联的是 执行前执行 在函数或脚本之后继续。

最后一条命令的返回值/退出码是通过$?获得的。

您的意思的关键字是命令替换。再次'man bash':

命令替换 命令替换允许命令的输出替换 命令名称。有两种形式:

          $(command)
   or
          `command`

Bash 通过执行命令并将命令替换替换为标准来执行扩展 命令的输出,删除任何尾随的换行符。嵌入式换行符 不会被删除,但它们可能会被删除 在分词时删除。 命令替换 $(cat file) 可以替换为 等效但更快 $(

   When  the old-style backquote form of substitution is used,

反斜杠保留其字面意义 除非后面跟着 $, `, 要么 。前面没有反斜杠的第一个反引号终止 命令替换。使用时 $(命令)形式, 括号之间的所有字符组成命令;没有 受到特殊对待。

   Command substitutions may be nested.  To nest when using the

反引号形式,转义内部 带反斜杠的反引号。

如果替换出现在双引号内,则分词 和路径名扩展不是 执行结果。

【讨论】:

【参考方案6】:

如果您在> /dev/null 之后仍然得到输出,那么它就会出现在标准错误中,因此标准反引号或$() 将不起作用。

首先,查看返回码是否表明有问题:检查 $?在成功和失败之后。

如果返回码不够,你可以重定向输出:

RETURNVALUE=$(google youtube post --access unlisted --category Tech $f 2>&1 >/dev/null)

2>&1 将 stderr 放在 stdout fd 上,然后再将 stdout 重定向为空。

【讨论】:

你是对的,道格拉斯。它出现在标准错误上。我没想到会这样,因为在这种情况下,stderr 上也会出现成功消息。对我来说这没有多大意义。反正就是这样。【参考方案7】:

您可以使用反引号将其分配给变量:

CONTENT=`google youtube post --access unlisted --category Tech $f > /dev/null`

【讨论】:

因为 stdout 被重定向了,所以你不会捕获任何东西。 啊,是的,您需要 2>&1 将 stderr 重定向到 stdout。

以上是关于想要通过将输出重定向到变量来检查命令是不是成功的主要内容,如果未能解决你的问题,请参考以下文章

显示 Windows 命令提示符输出并将其重定向到文件

Linux编程 22 shell编程(输出和输入重定向,管道,数学运算命令,退出脚本状态码)

sudo 与输出重定向

将 cmd 中命令的输出重定向到 Python 3 中的变量

输出表单提交成功到页面而不是重定向

Shell重定向