如何从 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
请澄清以下内容
- 为什么
awk
在这里表现得很奇怪?我缺少什么?
- 如何 grep 查找包含特殊字符的字符串中的模式?
- Ghostscript 是否有任何预定义的错误消息?如果可能,请建议一些文档以供参考..
- 是否可以使用 ghostscript 压缩密码保护的 pdf?
- 在上述情况下如何确保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。
我正在使用 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
请澄清以下内容
- 为什么
awk
在这里表现得很奇怪?我缺少什么? - 如何 grep 查找包含特殊字符的字符串中的模式?
- Ghostscript 是否有任何预定义的错误消息?如果可能,请建议一些文档以供参考..
- 是否可以使用 ghostscript 压缩密码保护的 pdf?
- 在上述情况下如何确保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 所述,如果您想解释导致问题的实际脚本是什么,也许我们也可以提供帮助。您没有显示脚本的输出,也没有解释什么不符合您的预期。
这是您的代码的简化版本,应该可以工作:
# 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 AwkGNU Awk 4.1.3, API: 1.1 ...
-> GNU Awkmawk: 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。