Альтернативы копрок и перенаправление подпроцесса (Bash 3)

Итак, я нахожусь в ситуации, когда я действительно хотел бы использовать либо совместный процесс через coproc или с помощью перенаправления, такого как <(some command), но, к сожалению, я ограничен bash 3.2 в одной из моих целевых сред, что означает, что я ограничен в том, что я могу сделать.

Причина, по которой мне нужен совместный процесс, заключается в том, что мне нужно читать построчно из одного файла, циклически перебирая другой.

В настоящее время я использую exec <6 /foo/bar создать оставить файл открытым для чтения, чтобы я мог сделать read line <&6 всякий раз, когда мне нужно больше информации. Это отлично работает, но работает только с простыми текстовыми файлами, однако на самом деле я бы хотел сжать мои файлы, а не распаковывать их перед запуском моего скрипта.

Мне также нужно иметь возможность сделать то же самое для записи в новый сжатый файл без необходимости тратить место на запись в виде простого текста и последующее сжатие.

Итак... есть ли альтернативы в Bash 3? Как я уже отмечал, я уже зациклен на другом файле, поэтому у меня нет возможности просто передать свой вывод в gzip (или трубопровод zcat в моем цикле), так как мне нужно сделать это независимо от моего цикла.

Чтобы попытаться привести пример, вот урезанная версия того, что я делаю сейчас:

# Decompress compressed match-file
gzip -dc /foo/compressed.gz > /tmp/match

# Setup file handles (to keep files open for reading/writing)
exec 5< /tmp/match
exec 6> /tmp/matches

# Loop over input file (/foo/bar) for matches
read next_match <&5
while read line; do
    if [ "$line" = "$next_match" ]; then
        read next_match <&5
        echo "$line" >&6
    fi

    echo "$line"
done < /foo/bar

# Close file handles
exec <5&-
exec 6>&-
rm /tmp/match

# Compress matches and overwrite old match file
gzip -cf9 /tmp/matches /foo/compressed.gz
rm /tmp/matches

Простите за любые опечатки и общую бесполезность самого сценария, я просто хотел сделать его довольно простым. Как видите, хотя он работает нормально, он не совсем оптимален благодаря расточительным текстовым файлам.

2 ответа

Решение

Вы можете использовать mknod создавать трубы и позволить gzip запись / чтение в фоновых процессах. Следующее, кажется, работает для меня:

#!/bin/bash

# create test files (one character per line)
echo abcdefgh | grep -o . | gzip > /tmp/foo.gz
echo aafbchddjjklsefksi | grep -o . > /tmp/bar

# create pipes for zipping an unzipping
PIPE_GUNZIP=/tmp/$$.gunzip
PIPE_GZIP=/tmp/$$.gzip
mkfifo "$PIPE_GUNZIP"
mkfifo "$PIPE_GZIP"

# use pipes as endpoints for gzip / gunzip
gzip -dc /tmp/foo.gz > "$PIPE_GUNZIP" &
GUNZIP_PID=$!
gzip -c9 > /tmp/foo.gz.INCOMPLETE < "$PIPE_GZIP" &
GZIP_PID=$!

exec 5< "$PIPE_GUNZIP"
exec 6> "$PIPE_GZIP"

read next_match <&5
while read line; do
    if [ "$line" = "$next_match" ]; then
        read next_match <&5
        echo "$line" >&6
    fi

    echo "$line"
done < /tmp/bar

# Close file handles
exec 5<&-
exec 6>&-

# wait for gzip to terminate, replace input with output, clean up
wait $GZIP_PID
mv /tmp/foo.gz.INCOMPLETE /tmp/foo.gz
rm "$PIPE_GZIP"

# wait for gunzip to terminate, clean up
wait $GUNZIP_PID
rm "$PIPE_GUNZIP"

# check result
ls -l /tmp/{foo,bar}*
gzip -dc /tmp/foo.gz

Поскольку процесс замены доступен в bash 3.2, вы можете просто использовать его.

# Setup file handles (to keep files open for reading/writing)
exec 5< <( gzip -dc /foo/compressed.gz )
exec 6> >( gzip -c9 /foo/new_compressed.gz)

# Loop over input file (/foo/bar) for matches
read next_match <&5
while read line; do
    if [ "$line" = "$next_match" ]; then
        read next_match <&5
        echo "$line" >&6
    fi

    echo "$line"
done < /foo/bar

# Close file handles
exec <5&- 6>&-

# Overwrite old match file
mv /foo/new_compressed.gz /foo/compressed.gz
Другие вопросы по тегам