管道/重定向一组命令


8

我目前使用以下设置来重定向多个命令的输出:

echo "Some normal commands"
(
echo "Error: something happened"
echo "Warning: this incident will be logged"
) >> logfile
echo "More normal commands"

这非常有用,并且还可以与管道一起使用。

这是最好的方法吗?我是否应该考虑替代方案?


我就是那样做的。您是否遇到这种方法的特定问题?
布拉奇利2014年

@JoelDavis Nope,只是想知道是否有更好的方法。从我收到的答案来看,好像已经存在了!:)
wchargin 2014年

Answers:


15

另一种方法是使用大括号而不是括号。此更改将在当前外壳程序而不是外壳程序中执行命令

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

太好了,谢谢!这对于我的缩进效果也更好。(Vim缩进,{ }但不是( )。)
wchargin

2
请注意,您可以通过两种方式将命令组折叠为一行。例如,(echo msg1; echo msg2)–但必须带花括号,必须在{ echo msg1; echo msg2;}后面有一个空格,{并在前面加上一个分号(;)或一个&符号(&}
G-Man说'Resstate Monica''2014年

4

格伦的回答是一个很好的-之间的区别( ... ){ ... }是很重要的。

我经常用于错误输出的一种策略(例如您的问题中的内容)是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
wchargin 2014年

另外,可能对以下内容感兴趣ts(1):用法:{ printf '%s\n' "Warning text"; printf '%s\n' "This event will be logged"; } | ts '[%Y-%m-%d %T]' | tee -a "$logfile" >&2
wchargin '16

@wchargin- ts(1)我使用的系统上未安装-FreeBSD ,OSX和旧的Ubuntu盒。你能告诉我们提供什么吗?
ghoti

它是moreutils的一部分,还包括一些不错的工具,例如sponge(1)(仅在关闭stdin后才写入文件,因此您something < foo | sponge foo无需foo重定向就不会造成麻烦),以及vipe(1)(将文本编辑器插入管道)。
wchargin '16

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; }
CMCDragonkai,2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.