한 번에 바이트 수와 sha1sum을 얻는 방법은 무엇입니까?

한 번에 바이트 수와 sha1sum을 얻는 방법은 무엇입니까?

명령 출력의 바이트 수와 sha1sum을 얻고 싶습니다.

원칙적으로는 항상 다음과 같이 할 수 있습니다.

BYTES="$( somecommand | wc -c )"
DIGEST="$( somecommand | sha1sum | sed 's/ .*//' )"

...그러나 내가 관심 있는 사용 사례의 경우 somecommand시간이 많이 걸리고 많은 출력을 생성하므로 한 번만 호출하는 것이 좋습니다.

제가 생각한 방법 중 하나는 바로 이것입니다

evil() {
  {
    somecommand | \
      tee >( wc -c | sed 's/^/BYTES=/' ) | \
      sha1sum | \
      sed 's/ .*//; s/^/DIGEST=/'
  } 2>&1
}

eval "$( evil )"

...효과가 있는 것 같았지만 속으로는 죽을 것 같았습니다.

파이프라인의 여러 세그먼트의 출력을 별도의 변수로 캡처하는 더 나은(더 강력하고 더 일반적인) 방법이 있는지 궁금합니다.

편집: 현재 해결 중인 문제는 bash이 셸 솔루션에 가장 관심이 있기 때문에 프로그래밍도 많이 수행하므로 zsh해당 솔루션에도 관심이 있습니다.

EDIT2: Stéphane Chazelas의 솔루션을 로 포팅하려고 시도했지만 bash제대로 작동하지 않았습니다.

#!/bin/bash

cmd() {
    printf -- '%1000s'
}

bytes_and_checksum() {
    local IFS
    cmd | tee >(sha1sum > $1) | wc -c | read bytes || return
    read checksum rest_ignored < $1 || return
}

set -o pipefail
unset bytes checksum
bytes_and_checksum "$(mktemp)"
printf -- 'bytes=%s\n' $bytes
printf -- 'checksum=%s\n' $checksum

위 스크립트를 실행하면 내가 얻는 결과는 다음과 같습니다.

bytes=
checksum=96d89030c1473585f16ec7a52050b410e44dd332

값이 checksum정확합니다. 왜 bytes값이 설정되어 있지 않은지 이해가 되지 않습니다 .

EDIT3: 좋습니다. @muru의 팁 덕분에 문제를 해결했습니다.

#!/bin/bash

cmd() {
    printf -- '%1000s'
}

bytes_and_checksum() {
    local IFS
    read bytes < <( cmd | tee >(sha1sum > $1) | wc -c ) || return
    read checksum rest_ignored < $1 || return
}

set -o pipefail
unset bytes checksum
bytes_and_checksum "$(mktemp)"
printf -- 'bytes=%s\n' $bytes
printf -- 'checksum=%s\n' $checksum

지금:

bytes=1000
checksum=96d89030c1473585f16ec7a52050b410e44dd332

안타깝게도...

bytes_and_checksum... 위 예제의 경우보다 훨씬 더 많은 출력을 생성하는 경우 내 함수가 중단됩니다(교착 상태?).cmd

다시 드로잉 보드로 돌아가서...

답변1

임시 파일을 사용하는 것이 더 쉽습니다. 존재하다 zsh:

(){set -o localoptions -o pipefail; local IFS
  {cmd} > >(sha1sum > $1) | wc -c | read bytes || return
  read checksum rest_ignored < $1 || return
} =()

많은 wc구현에는 출력하는 숫자 주위에 공백이 포함되어 있습니다. read기본값은 $IFS제거하는 것입니다.

의 종료 상태가 sha1sum손실됩니다.

임시 파일이 생성될 =()때 내용이 전혀 출력되지 않습니다 . 임시 파일은 제공된 명령(여기서는 익명 함수)이 반환되면 자동으로 삭제됩니다.

의 출력 은 두 번 리디렉션되므로 d에 의해 내부적으로 처리되므로 cmd > file | other-cmd여기 에는 to 및 to 가 있습니다 . zsh가 프로세스 리디렉션이 완료될 때까지 기다리도록 래핑 합니다 .cmdteezshsha1sumwccmd{...}

여기서 두 출력은 몇 바이트 이하로 보장 sha1sum되며 wc파이프로 전송될 수도 있으며 동시에 해당 파이프에서 읽을 필요가 없습니다(zsh는 다음을 select()위한 인터페이스를 가지고 있으므로 이를 수행할 수 있습니다). / poll(), 그러나 bash는 아님). 이는 교착 상태를 일으키지 않고 순차적으로 수행될 수 있으므로 여기에 간단한 버전이 있습니다.다양한 변수로.

Linux 기반 시스템( /dev/fd/x파이프 x의 fd가 명명된 파이프처럼 동작하는 경우):

{
  IFS=$' \t' read bytes < <(cmd 3<&- | tee >(sha1sum > /dev/fd/3) | wc -c)
  IFS=$' \t' read sum rest <&3
} 3< <(:)

(Bash에서도 작동합니다).

더 큰 출력에서 ​​발생하는 교착 상태에 대한 자세한 내용은 다음을 참조하세요.tee + cat: 출력을 여러 번 사용한 다음 결과를 연결합니다..

답변2

백업을 사용하고 있습니다세게 때리다"가정된 파일 이름"을 인수로 사용하는 다음 도우미 "중간" 함수가 있는 스크립트(아래 tar.gz 예제 참조):

function pipesum
{
  tee >(sha1sum | awk --assign F="${1##*/}" '$2=F' > "${1?}.sha1")
}
function pipelen
{
  tee >(wc -c > "${1?}.len")
}
function pipesumlen
{
  tee >(sha1sum | awk --assign F="${1##*/}" '$2=F' > "${1?}.sha1") >(wc -c > "${1?}.len")
}
function pipechecksum
{
  tee >(sha1sum --quiet -c <(awk '$2="-"' "${1?}") >&2)
}

예:

$ echo 123 | pipesumlen filename
123
$ ls filename*
filename.len  filename.sha1
$ cat filename*
4
a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0 filename
$ echo 123 | pipechecksum filename.sha1
123
$ echo 1234 | pipechecksum filename.sha1
1234
-: FAILED
sha1sum: WARNING: 1 computed checksum did NOT match

나는 이것을 다음과 같이 시간이 많이 걸리고 CPU 및 IO를 많이 소비하는 스크립트에서 사용하고 있습니다.

tar | 
  pipesumlen mybackup.tar | 
  gzip > mybackup.tar.gz
<mybackup.tar.gz gunzip | 
  pipechecksum mybackup.tar.sha1 | 
  xz > mybackup.tar.xz

그래서 무작위 메모리/디스크 비트 플립을 기반으로 백업을 확인했습니다. "mybackup.tar"가 실제로 생성되고 체크섬되는 것처럼 "mybackup.tar.sha1" 파일이 생성됩니다. 실제로 이 예에서는 압축되지 않은 데이터가 디스크에 기록되지 않습니다.

경고: pipechecksum오류가 발생하더라도 스크립트는 종료되지 않습니다 set -euo pipefail. pipechecksum체크섬이 일치하지 않을 때 0이 아닌 값을 반환하는 대안:

function pipechecksum
{
  { tee /dev/fd/$N | sha1sum --quiet -c <(awk '$2="-"' "${1?}") >&2; } {N}>&1
}

좋아 보이는데, 오늘 막 가져왔는데 아직 확정된 것으로 볼 수는 없습니다.

관련 정보