일련의 명령을 파이프/리디렉션

일련의 명령을 파이프/리디렉션

현재 다음 설정을 사용하여 여러 명령의 출력을 리디렉션합니다.

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

이것은 매우 유용하며 파이프에서도 작동합니다.

이것이 최선의 방법입니까? 고려해야 할 다른 옵션이 있습니까?

답변1

또 다른 방법은 괄호 대신 중괄호를 사용하는 것입니다. 이 변경을 실행하는 명령현재의쉘, 거기 없어서브쉘

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

답변2

Glenn의 답변은 훌륭합니다. ( ... )와 의 차이점이 { ... }중요합니다.

귀하의 질문과 같은 오류 출력에 자주 사용하는 전략 중 하나는 Commands 입니다 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"append" 옵션은 지정된 파일에 출력을 보내고 명령은 입력도 표준 출력으로 전달합니다. >&2줄 끝에서 stdout을 teestderr로 리디렉션하며 이는 다르게 처리될 수 있습니다(예: cron 작업에서).

쉘 스크립트에서 자주 사용하는 또 다른 방법은 스크립트가 터미널에서 실행되는지 또는 옵션이 제공되는지에 따라 디버깅 동작이나 자세한 출력을 변경하는 것입니다 -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

스크립트는 상단에 이와 같은 일반적인 내용으로 시작할 수 있으며, 자세한 내용과 디버그 출력은 스크립트 전체에 분산되어 있습니다. 이것은 단지 하나의 접근 방식일 뿐입니다. 접근 방식은 다양하며 사람들은 이러한 문제에 접근하는 자신만의 방식을 가지고 있습니다. 특히 한동안 사용했다면 더욱 그렇습니다. :)

또 다른 옵션은 보다 지능적인 작업을 수행할 수 있는 셸 기능인 "핸들러"를 사용하여 출력을 처리하는 것입니다. 예를 들어:

#!/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기능을 사용할 수 있는 쉘 함수가 생성됩니다( logme()` logger명령 사용 ) to send things to system logs. The). 이 함수는 로그 데이터의 한 줄을 생성하는 옵션과 함께 사용되거나 stdin에서 처리되는 여러 줄의 입력과 함께 사용될 수 있습니다. 매력적으로 보이기 위해 사용할 수 있습니다.

이는그리고 당신이 그것을 이해하고 그것이 당신에게 필요한 것임을 정확히 알지 않는 한 축어적으로 복사해서는 안 됩니다. 더 나은 아이디어는 여기에서 개념을 취하고 자신의 스크립트에서 직접 구현하는 것입니다.

답변3

보다 적절한 접근 방식은 { command; }대신 을 사용하는 것입니다 (command). 그 이유는 명령이 ()서브쉘로 그룹화되면 명령을 실행하기 위해 서브쉘이 열리므로 해당 블록 중에 초기화된 변수는 스크립트의 다른 부분에서 사용할 수 없기 때문입니다.

대조적으로, 명령 그룹화를 사용하면 {}명령이 동일한 셸에서 실행되므로 스크립트의 다른 부분에서 변수를 사용할 수 있습니다.

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변수는 그 값을 유지하지만 다른 경우에는 그렇지 않습니다.

관련 정보