如何从 shell 脚本中的命令输出中获取字符串模式?

Posted

技术标签:

【中文标题】如何从 shell 脚本中的命令输出中获取字符串模式?【英文标题】:How to grep for a string pattern from command output in shell script? 【发布时间】:2016-11-19 11:58:04 【问题描述】:

我正在使用 ghostscript 压缩我的 pdf 文件,这会在我必须处理的受密码保护的情况下引发错误。

Shell 脚本

GS_RES=`gs -sDEVICE=pdfwrite -sOutputFile=$gsoutputfile -dNOPAUSE -dBATCH $2 2>&1`

if [ "$GS_RES" != "" ]
then
    gspassmsg="This file requires a password for access"
    echo "Error message is :::::: "$GS_RES
    gspassworddoc=`awk -v a="$GS_RES" -v b="$gspassmsg" 'BEGINprint index(a,b)'`
    if [ $gspassworddoc -ne 0 ]
    then
        exit 3 #error code - password protected pdf
    fi
fi

而我的GS_RES执行命令后的值如下

错误信息 1:

GPL Ghostscript 9.19 (2016-03-23) Copyright (C) 2016 Artifex Software, Inc. All 
rights reserved. This software comes with NO WARRANTY: see the file PUBLIC for d
etails. Error: /syntaxerror in -file- Operand stack: Execution stack: %interp_ex
it .runexec2 --nostringval-- --nostringval-- --nostringval-- 2 %stopped_push --n
ostringval-- --nostringval-- --nostringval-- false 1 %stopped_push 1967 1 3 %opa
rray_pop 1966 1 3 %oparray_pop 1950 1 3 %oparray_pop 1836 1 3 %oparray_pop --nos
tringval-- %errorexec_pop .runexec2 --nostringval-- --nostringval-- --nostringva
l-- 2 %stopped_push Dictionary stack: --dict:1196/1684(ro)(G)-- --dict:0/20(G)--
 --dict:78/200(L)-- Current allocation mode is local Current file position is 1

错误消息 2:

GPL Ghostscript 9.19 (2016-03-23) Copyright (C) 2016 Artifex Software, Inc. All rights reserved. This software comes with NO WARRANTY: see the file PUBLIC for details. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html Error: Cannot find a 'startxref' anywhere in the file. Output may be incorrect. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html Error: An error occurred while reading an XREF table. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html The file has been damaged. This may have been caused gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html by a problem while converting or transfering the file. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html Ghostscript will attempt to recover the data. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html However, the output may be incorrect. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html Error: Trailer dictionary not found. Output may be incorrect. No pages will be processed (FirstPage > LastPage). gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html This file had errors that were repaired or ignored. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html Please notify the author of the software that produced this gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html file that it does not conform to Adobe's published PDF gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html specification. gs.pdf gsempty.pdf new_sathishks_protected.html sathishks_protected.html The rendered output from this file may be incorrect.

在错误消息 2 上运行 awk

gspassmsg="This file requires a password for access"
gspassworddoc=`awk -v a="$GS_RES" -v b="$gspassmsg" 'BEGINprint index(a,b)'`

它会抛出以下错误

错误:awk: newline in string GPL Ghostscript 9.19... at source line 1

错误信息 3

   **** Error: Cannot find a 'startxref' anywhere in the file.
   **** Warning:  An error occurred while reading an XREF table.
   **** The file has been damaged.  This may have been caused
   **** by a problem while converting or transfering the file.
   **** Ghostscript will attempt to recover the data.
   **** Error:  Trailer is not found.

   **** This file had errors that were repaired or ignored.
   **** Please notify the author of the software that produced this
   **** file that it does not conform to Adobe's published PDF
   **** specification.

我无法使用以下答案中的 sn-p 捕获此错误

if ! gs_res=$(gs -sDEVICE=pdfwrite -sOutputFile="$gsoutputfile" -dNOPAUSE -dBATCH "$2" 2>&1 1>/dev/null); then
  echo "Error message is :::::: $gs_res" >&2
  gspassmsg='This file requires a password for access'
  [[ $gs_res == *"$gspassmsg"* ]] && exit 3 # password protected pdf
  echo "Some other error !"
fi

请澄清以下内容

    为什么awk 在这里表现得很奇怪?我错过了什么? 如何在包含特殊字符的字符串中查找模式? Ghostscript 是否有任何类似的预定义错误消息?如果可能,请建议一些文档以供参考.. 是否可以使用 ghostscript 压缩受密码保护的 pdf? 在上述情况下如何确保 gs 压缩成功?由于我可能不知道 Ghostscript 可能抛出的不同可能错误,因此我可以与我执行的命令结果进行交叉检查。

我对这个 shell 脚本很陌生。有人请帮我解决这个问题。

PS:我已经用其他详细信息编辑了我的问题。请调查一下。如果有什么需要补充的,我会补充的。

【问题讨论】:

@mklement0 从命令输出中搜索字符串(例如“此文件需要密码才能访问”) - 将此过程称为grep 当我的命令输出中包含一些特殊字符时,我不确定我猜awk 会播放有线。 命令输出中的特殊字符类似于 (`, ") - 我已在错误消息 2 中添加。请查看。 你是对的@mklement0。听起来不错!!超级:+1: @subramanianrasapan:很高兴听到这个消息。我已经意识到这不仅仅是index():当您尝试将多行字符串作为变量值传递时,BSD Awk 从根本上失败,除非您\ -转义换行符。我的回答中有详细信息和替代解决方案。 【参考方案1】:

KenS's helpful answer 解决您关于 Ghostscript 本身的问题。 这是您的代码的简化版本,应该可以工作:

# Run `gs` and capture its stderr output.
gs_res=$(gs -sDEVICE=pdfwrite -sOutputFile="$gsoutputfile" -dNOPAUSE -dBATCH "$2" 2>&1 1>/dev/null)
ec=$? # Save gs's exit code.

# Assume that something went wrong, IF:
#   - gs reported a nonzero exit code
#   - but *also* if any stderr output was produced, as
#     not all problems may be reflected in a nonzero exit code.
if [[ $ec -ne 0 || -n $gs_res ]]; then
  echo "Error message is :::::: $gs_res" >&2
  gspassmsg='This file requires a password for access'
  [[ $gs_res == *"$gspassmsg"* ]] && exit 3 # password protected pdf
fi

我在您的gs command 中引用了变量和参数引用。

我已将您的重定向从 2>&1 更改为 2>&1 1>/dev/null,以便捕获标准错误输出。

2>&1 将 stderr (2) 重定向到(仍然是原始的)stdout (1),以便将错误消息发送到 stdout,并且可以作为命令替换 ($(...)) 的一部分捕获; 1>/dev/null 然后将 stdout 重定向到 null 设备,有效地使所有 stdout 输出静音。请注意,stderr 到 original stdout 的 earlier 重定向不受此影响,因此实际上整个命令发送到 stdout 的内容是仅原始标准错误输出。 如果您想了解更多信息,请参阅我的this answer。

我正在使用更现代、更灵活的$(..) 命令替换语法,而不是传统的`...` 形式(有关背景信息,请参阅here)。

我已将 GS_RES 重命名为 gs_res,因为最好不要使用全大写的 shell 变量名称来代替 avoid conflicts with environment variables and special shell variables。

我正在使用简单的模式匹配在gs 的标准错误输出中找到所需的子字符串。鉴于您已经在变量中拥有要测试的输入,Bash 自己的字符串匹配功能就可以了(实际上是多种多样的),并且不需要使用诸如 awk 之类的外部实用程序。


至于为什么你的awk 命令失败

听起来你正在使用 BSD awk,例如 macOS 10.12 附带的那个(但是你的问题被标记为 linux):

BSD awk 不支持通过 -v 传递的变量值中的换行符,除非您 \-转义换行符。 使用未转义的多行字符串,在调用 index() 之前,您的 awk 调用基本上会失败。

相比之下,GNU Awk 和 Mawk 确实支持通过 -v 传递的多行字符串。

继续阅读可选背景信息


要确定您使用的是哪个awk 实现,请运行awk --version 并检查输出:

awk version 20070501 -> BSD awk

GNU Awk 4.1.3, API: 1.1 ... -> GNU awk

mawk: not an option: --version -> 毛克

这里有一个简单的测试,可以尝试使用您的 awk 版本:

awk -v a=$'1\n2' -v b=2 'BEGIN  print index(a, b) '

Gnu Awk 和 Mawk 按预期输出 3,而 BSD Awk 失败并显示 awk: newline in string 1

另请注意,\-escaping newlines 仅适用于 BSD Awk(例如,awk -v var=$'1\\\n2' 'BEGIN print var '),不幸的是,这意味着 没有可移植的方式来传递 multi -line 变量值到 Awk.

【讨论】:

感谢您抽出宝贵时间@mklement0。如果我不问更多,你能解释一下2>&1 1>/dev/null 的作用吗?我对此没有任何想法。请帮我了解一下。 我也在 linux 和 centos 中使用了这个awk @subramanianrasapan:回复2>&1 1>/dev/null:请看我的更新。 @subramanianrasapan:我提到了awk 中的哪一个?运行awk --version 会得到什么?另请记住,没有必要使用awk 来解决您的问题,如我修改后的代码所示。 我的“awk”版本返回为 GNU awk 4.0.2【参考方案2】:

Ghostscript 的错误消息都遵循相同的模式,但是有一些陷阱:

部分输出是错误发生时操作数堆栈的转储。由于 PostScript 是一种编程语言,堆栈的内容取决于程序,并且是完全不可预测的。即使您处理的是 PDF 文件,而不是 PostScript 程序,解释器本身也是用 PostScript 编写的,所以同样适用。

'错误:/syntaxerror...'

仅限于少数实际可能出现的错误,PostScript 语言参考手册对它们进行了定义。

PostScript(但不是 PDF)程序可以安装错误处理程序,它可以完全改变错误输出,甚至完全吞下错误。

至于“压缩 PDF 文件”,这绝对是不是您正在做的事情。请阅读here,它解释了实际发生的情况。简而言之,您正在生成一个新的 PDF 文件,而不是压缩旧的。

当然,只要您知道密码,您就可以使用 Ghostscript 处理受密码保护的 PDF 文件。在文档中查找 PDFPassword here

现在您在上面引用的错误消息是不是,因为文件被加密(密码保护),还有其他问题。事实上,鉴于您使用的简单命令行,我会说它有一些非常严重的错误。当然没有看到文件我无法确定。

现在如果文件被加密,Ghostscript 的输出应该是这样的:

GPL Ghostscript GIT PRERELEASE 9.21 (2016-09-14) 版权所有 (C) 2016 Artifex Software, Inc. 保留所有权利。 此软件不提供任何保证:请参阅文件 PUBLIC 了解详细信息。

**** 此文件需要密码才能访问。

错误:pdf_process_Encrypt 中的 /invalidfileaccess

操作数栈:

执行堆栈:%interp_exit .runexec2 --nostringval-- --nostringval-- --nostringval- - 2 %stopped_push --nostringval-- --nostringval-- --nostringval-- 错误 1 ​​%stopped_push 1983 1 3 %oparray_pop 1982 1 3 %oparray_pop 1966 1 3 %oparray_pop --nostringval-- --nostringval-- --nostri ngval-- --nostringval-- false 1 %stopped_push 字典栈:--dict:1199/1684(ro)(G)-- --dict:1/20(G)-- --dict:83/200(L)- - --dict:83 /200(L)-- --dict:135/256(ro)(G)-- --dict:291/300(ro)(G)-- --dict:26/32 (L)- - 当前分配模式是本地 GPL Ghostscript GIT PRERELEASE 9.21: Unrecoverable error, exit code 1

所以简单地搜索“此文件需要密码”就足以识别加密文件。

现在,正如 mklement0 所指出的,如果您想解释导致问题的实际脚本是什么,也许我们也可以提供帮助。您没有显示您的脚本的输出,或解释什么没有按您预期的那样工作。

【讨论】:

以上是关于如何从 shell 脚本中的命令输出中获取字符串模式?的主要内容,如果未能解决你的问题,请参考以下文章

如何将一shell脚本中的每一步命令执行结果输出到指定日志文件中

如何从在 C 中存储 shell 脚本输出的指针获取多个字符串?

检查命令的输出是不是包含 shell 脚本中的某个字符串

编写一个shell脚本,输出所有尾数是5的三位数

如何从 shell 获取准确的命令行字符串?

shell脚本调JAVA程序,获取JAVA程序返回值并echo输出