如何从 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脚本中的每一步命令执行结果输出到指定日志文件中