我目前使用以下设置来重定向多个命令的输出:
echo "Some normal commands"
(
echo "Error: something happened"
echo "Warning: this incident will be logged"
) >> logfile
echo "More normal commands"
这非常有用,并且还可以与管道一起使用。
这是最好的方法吗?我是否应该考虑替代方案?
:)
我目前使用以下设置来重定向多个命令的输出:
echo "Some normal commands"
(
echo "Error: something happened"
echo "Warning: this incident will be logged"
) >> logfile
echo "More normal commands"
这非常有用,并且还可以与管道一起使用。
这是最好的方法吗?我是否应该考虑替代方案?
:)
Answers:
另一种方法是使用大括号而不是括号。此更改将在当前外壳程序而不是子外壳程序中执行命令
echo "Some normal commands"
{
echo "Error: something happened"
echo "Warning: this incident will be logged"
} >> logfile
echo "More normal commands"
参考:https : //www.gnu.org/software/bash/manual/bashref.html#Command-Grouping
在修改组内的变量时,这尤其重要:
$ x=5; ( x=10; echo inside: $x; ); echo outside: $x
inside: 10
outside: 5
$ x=5; { x=10; echo inside: $x; }; echo outside: $x
inside: 10
outside: 10
{ }
但不是( )
。)
(echo msg1; echo msg2)
–但必须带花括号,必须在{ echo msg1; echo msg2;}
后面有一个空格,{
并在前面加上一个分号(;
)或一个&符号(&
)}
。
格伦的回答是一个很好的-之间的区别( ... )
和{ ... }
是很重要的。
我经常用于错误输出的一种策略(例如您的问题中的内容)是tee
命令。您可以执行以下操作:
echo "Normal output"
{
printf "[%s] %s\n" "$(date '+%Y-%m-%d %T')" "Warning text"
printf "[%s] %s\n" "$(date '+%Y-%m-%d %T')" "This event is logged."
} | tee -a $logfile >&2
echo "More normal output"
该tee
命令会将输出发送到两个位置;-a
选项“追加”输出到命名文件,该命令还将输入传递到stdout。的>&2
在该行重定向的端部tee
的标准输出到stderr,其可以被不同地处理(即,在定时任务)。
我经常在shell脚本中使用的另一个技巧是根据脚本是在终端上运行还是提供-v
选项来更改调试或详细输出的行为。例如:
#!/bin/sh
# Set defaults
if [ -t 0 ]; then
Verbose=true; vflag="-v"
else
Verbose=false; vflag=""
fi
Debug=false; AskYN=true; Doit=true
# Detect options (altering defaults)
while getopts vdqbn opt; do
case "$opt" in
v) Verbose=true; vflag="-v" ;; # Verbose mode
d) Debug=true; Verbose=true; vflag="-v" ;; # Very Verbose
q) Verbose=false; vflag="" ;; # quiet mode (non-verbose)
b) AskYN=false ;; # batch mode
n) Doit=false ;; # test mode
*) usage; exit 1 ;;
esac
done
# Shift our options for further processing
shift $(($OPTIND - 1))
$Verbose && echo "INFO: Verbose output is turned on." >&2
$Debug && echo "INFO: In fact, expect to be overrun." >&2
# Do your thing here
if $AskYN; then
read -p "Continue? " choice
case "$choice" in
Y|y) $Doit && somecommand ;;
*) echo "Done." ;;
esac
fi
脚本可以从顶部这样的通用类开始,而Verbose和Debug输出遍布整个脚本。这只是做到这一点的一种方法-有很多,而且不同的人都会有自己的方式来处理这些东西,特别是如果他们已经有一段时间了。:)
还有一个选择是使用“处理程序”来处理您的输出,该处理程序是一个可以执行更多智能操作的shell函数。例如:
#!/bin/bash
logme() {
case "${1^^}" in
[IN]*) level=notice ;;
W*) level=warning ;;
A*) level=alert ;;
E*) level=emerg ;;
*) level=notice ;;
esac
if [[ "$#" -eq 1 ]]; then
# Strip off unnecessary prefixes like "INFO:"
string="${1#+([A-Z])?(:) }"
else
shift
string="$@"
fi
logger -p "${facility}.${level}" -t "$(hostname -s)" "$string"
}
echo "Normal output"
logme INFO "Here we go..."
somecommand | logme
echo "Additional normal output"
(请注意,这${var^^}
仅适用于bash。)
这将创建一个可以使用您系统syslog
功能的shell函数(通过使用logme()logger
命令,) to send things to system logs. The
该函数既可以与生成单行日志数据的选项结合使用,也可以与在stdin上处理的多行输入结合使用。似乎很吸引人。
请注意,这是一个示例,除非您理解并知道它确实可以满足您的需要,否则不应逐字复制。一个更好的主意是在此处采用这些概念,并在自己的脚本中自行实现。
function log () { cat >> $logfile }
,我一直在使用,这基本上是您的的简单版本logme
。
ts(1)
:用法:{ printf '%s\n' "Warning text"; printf '%s\n' "This event will be logged"; } | ts '[%Y-%m-%d %T]' | tee -a "$logfile" >&2
。
ts(1)
我使用的系统上未安装-FreeBSD ,OSX和旧的Ubuntu盒。你能告诉我们提供什么吗?
sponge(1)
(仅在关闭stdin后才写入文件,因此您something < foo | sponge foo
无需foo
重定向就不会造成麻烦),以及vipe(1)
(将文本编辑器插入管道)。
更合适的方法是使用{ command; }
而不是(command)
。原因是,当命令与()
子shell 分组在一起时,将打开它们以执行那些命令,因此在该块中初始化的变量将不可用于脚本的其他部分。
相反,当我们{}
用于命令分组时,命令在同一shell中执行,因此变量将可用于脚本的其他部分。
echo "Some normal commands"
{
var=1
echo "Error: something happened"
echo "Warning: this incident will be logged"
} >> logfile
echo "The value of var is: $var"
echo "More normal commands"
此处,当执行此部分时,$var
变量将保留其值,而在其他情况下则不会。
{ command; }
。