bash 변수에 명령의 파일 출력을 저장합니다.

bash 변수에 명령의 파일 출력을 저장합니다.

동시에 다른 파일 foo에 쓰는 유틸리티가 있다고 가정해 보겠습니다 . n각 파일에는 서로 다른 데이터가 기록됩니다. 파일에 기록되는 내용은 미리 알 수 없습니다. 예를 들어 유틸리티는 다음과 같이 호출할 수 있습니다.

foo file_1 file_2 ... file_n

파일에 작성된 내용을 bash 변수에 저장하고 싶습니다. 물론, 유틸리티가 파일 시스템의 실제 파일에 쓴 다음 그 파일에서 읽도록 할 수도 있습니다.

foo file_1 ... file_n

output_1="$(cat file_1)"
...
output_n="$(cat file_n)"

rm file_1 ... file_n

다만, 파일 시스템을 건너뛰고 임시 파일 생성을 피하는 것이 더 효율적이지 않을까 생각합니다. 이것이 어떻게 달성될 수 있습니까? 이 목적으로 명명된 파이프를 사용할 수 있습니까?

답변1

이제 답을 찾은 것 같아요. @terdon 솔루션의 문제점은 @ilkkachu가 언급했듯이 파일이 순차적으로 작성된다고 가정한다는 것입니다. 먼저 file1완료될 때까지 읽은 다음 계속 읽습니다 file2. 그러나 와 동시에 foo쓰기 는 가능하며 , 리더기가 부착되어 있지 않아 차단됩니다.file1file2file2

이 문제는 호출 직후 읽기 위해 각 FIFO를 열고 foo순차적으로 읽으면 해결될 수 있습니다. 그러나 이는 foo쓰기가 FIFO 버퍼 크기를 초과하지 않는 경우에만 작동합니다.

따라서 각 파일/fifo를 동시에 읽어야 합니다. 각 fifo에 대해 백그라운드에서 실행되고 완료될 때까지 fifo에서 데이터를 읽는 하위 쉘을 만들 수 있습니다. 그런 다음 서브쉘은 내용을 stdout으로 인쇄합니다. 따라서 FIFO에 기록된 데이터에 대한 버퍼 역할을 합니다.

# assume files don't exist yet
mkfifo "$@"

# opens fifos as writing
foo "$@" &

# for each fifo, create background subshell that reads from it
i=0
for file in "$@"; do
    exec {fd}< <(echo "$(cat $file)")
    fds[$i]=$fd
    (( i++ ))
done

# fifos are now fully connected
# consume content buffered in subshells
i=0
for file in "$@"; do
    file_contents[i]="$(cat <&${fds[$i]})"
    (( i++ ))
done

for (( i=0; i<${#file_contents[@]}; i++)); do
  printf "The contents of file number %d are: %s\n" "$(( $i+1 ))" "${file_contents[$i]}"
done

답변2

예, 이것이 바로 명명된 파이프(FIFO라고도 함)의 용도입니다. 장난감의 예는 다음과 같습니다.

#!/bin/bash
i=0
for file in "$@"; do
  mkfifo "$file"
  printf "This is file '%s'\n" "$file" > "$file" &
  file_contents[i]=$(cat < "$file")
  rm "$file"
  (( ++i ))
done

for (( i=0; i<${#file_contents[@]}; i++)); do
  printf "The contents of file number %d are: %s\n" "$i" "${file_contents[i]}"
done

이것은 파일을 생성하고 삭제하는 것처럼 보이지만 공식적으로는 파이프 대신 명명된 파일입니다.정기적인파일이므로 실제로 데이터가 디스크에 기록되지 않습니다.

그러나 이는 여전히 파일 시스템을 사용하며 엄밀히 말하면 최소한으로만 사용합니다. 파일에는 내용이 없으므로 실제로 디스크에 아무것도 기록되지 않지만 이에 대한 파일 시스템 항목이 생성됩니다. 에서 man fifo:

DESCRIPTION
       A  FIFO  special file (a named pipe) is similar to a pipe, except that
       it is accessed as part of the filesystem.  It can be opened by  multi‐
       ple  processes  for reading or writing.  When processes are exchanging
       data via the FIFO, the kernel passes all data internally without writ‐
       ing it to the filesystem.  Thus, the FIFO special file has no contents
       on the filesystem; the filesystem entry merely serves as  a  reference
       point  so  that  processes  can  access  the  pipe using a name in the
       filesystem.

귀하의 경우에는 다음과 같이 할 수 있습니다.

#!/bin/bash

for file in "$@"; do
  ## CAREFUL: this will delete any files with the same name if they exist
  if [ -e "$file" ]; then
    rm -- "$file"
  fi
  mkfifo -- "$file"
done

## This is your utility
foo "$@" &

## And now read the variables
i=0
for file in "$@"; do
  file_contents[i]=$(cat < "$file")
  rm -- "$file"
  (( ++i ))
done

## And here you can use the file_contents array to do whatever you need
for (( i=0; i<${#file_contents[@]}; i++)); do
  printf "The contents of file number %d are: %s\n" "$i" "${file_contents[i]}"
done

실행하려면 foo file1 file2 file3스크립트를 file1 file2 file3인수로 실행하면 됩니다.

my_script.sh file1 file2 file3

관련 정보