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

How to grep for a string pattern from command output in shell script?

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

Shell 脚本

GS_RES=`gs -sDEVICE=pdfwrite -sOutputFile=$gsoutputfile -dNOPAUSE -dBATCH  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" 'BEGIN{print 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.

关于 运行 awk 关于错误消息 2

gspassmsg="This file requires a password for access"
gspassworddoc=`awk -v a="$GS_RES" -v b="$gspassmsg" 'BEGIN{print 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.

我无法使用以下答案中的片段捕获此错误

if ! gs_res=$(gs -sDEVICE=pdfwrite -sOutputFile="$gsoutputfile" -dNOPAUSE -dBATCH "" 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

请澄清以下内容

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

我对这个 shell 脚本很陌生。有人请帮助我。

PS:我已经用更多细节编辑了我的问题。请调查一下。有什么需要补充的我再补充。

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

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

'Error: /syntaxerror...'

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

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

至于'compressing PDF files',那绝对不是你在做什么。请阅读 here,其中解释了实际发生的情况。简而言之,您正在生成新的 PDF 文件,而不是压缩旧文件。

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

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

现在,如果文件已加密,Ghostscript 的输出应该如下所示:

GPL Ghostscript GIT PRERELEASE 9.21 (2016-09-14) Copyright (C) 2016 Artifex Software, Inc. All rights reserved. This software comes with NO WARRANTY: see the file PUBLIC for details.

**** This file requires a password for access.

Error: /invalidfileaccess in pdf_process_Encrypt

Operand stack:

Execution stack: %interp_exit .runexec2 --nostringval--
--nostringval-- --nostringval- - 2 %stopped_push --nostringval-- --nostringval-- --nostringval-- fa lse 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 Dictionary stack: --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)- - Current allocation mode is local GPL Ghostscript GIT PRERELEASE 9.21: Unrecoverable error, exit code 1

所以简单地搜索 "This file requires a password" 应该足以识别加密文件。

现在,如 mklement0 所述,如果您想解释导致问题的实际脚本是什么,也许我们也可以提供帮助。您没有显示脚本的输出,也没有解释什么不符合您的预期。

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

# Run `gs` and capture its stderr output.
gs_res=$(gs -sDEVICE=pdfwrite -sOutputFile="$gsoutputfile" -dNOPAUSE -dBATCH "" 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 以便 捕获 stderr 输出。

    • 2>&1 将 stderr (2) 重定向到(仍然是原始的)stdout (1),以便将错误消息发送到 stdout 并作为命令替换($(...)); 1>/dev/null 然后将 stdout 重定向到空设备,有效地使所有 stdout 输出静音。请注意 earlier stderr 重定向到 original stdout not 受此影响,因此实际上整个命令发送到 stdout 的只是原始的 stderr 输出。
      如果你想了解更多,请看我的this answer
  • 我正在使用更现代和灵活的 $(..) 命令替换语法,而不是旧的 `...` 形式(有关背景信息,请参阅 here) .

  • 我已经将GS_RES重命名为gs_res,因为最好不要使用全大写的shell-变量名以便avoid conflicts with environment variables and special shell variables.

  • 我正在使用简单的模式匹配在 gs 的 stderr 输出中查找所需的子字符串。鉴于您已经在变量中有要测试的输入,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

另请注意,\-转义换行符仅适用于 BSD Awk(例如,
awk -v var=$'1\\n2' 'BEGIN { print var }'),不幸的是,这意味着 没有可移植的方式将多行变量值传递给 Awk