Как мне обработать весь файл в одном буфере в sed без опции GNU -z?

Когда оболочка доберется до оператора for-, она расширит значение $search_dirи выполнит подстановку имени файла, чтобы сгенерировать список записей каталога, которые будут повторяться. Это происходит только один раз, и если вещи в $search_dirисчезнут или если в этот каталог будут добавлены новые файлы/каталоги во время выполнения цикла, эти изменения не будут приняты.

Если цикл работает с записями каталога, чьи имена находятся в $entry, может потребоваться проверить их существование в цикле, особенно если известно, что цикл выполняется долго и имеется много файлов. которые находятся в постоянном движении по той или иной причине:

for entry in "$search_dir"/*; do
    if [ -e "$entry" ]; then
        # operate on "$entry"
    else
        # handle the case that "$entry" went away
    fi
done

Как справедливо отмечает Стефан в комментариях, это лишний тест в большинстве случаев.

2
03.08.2019, 05:36
2 ответа

В чистом POSIX sedвы должны вставить все строки самостоятельно. В то время как некоторые люди делают это с помощью Nвнутри цикла, самый простой подход — добавить к пространству хранения шаблон H;1h;$!d;x:

  • Hдобавляет каждую строку в область хранения. К сожалению, добавление первой строки добавит новую строку в начало буфера, поэтому
  • 1hпереопределит пробел для первой строки, чтобы избежать неправильного перехода на новую строку.
  • $!dзавершит обработку всех строк, кроме последней. Их не нужно распечатывать,потому что они хранятся в резервном пространстве
  • xбудет выполнено только после последней строки (для всех остальных строк dостановит дальнейшую обработку команды )и xизменит пространство удержания и пространство шаблона, поэтому после этой команды вся файл, который был собран в пространстве хранения, будет находиться в пространстве шаблонов, точно так же, как это было бы с опцией -zGNU sed. Конечно, вы также можете использовать gвместо x, но это приведет к большому количеству копирования, поэтому xбыстрее.

Таким образом, скрипт для примера будет выглядеть так:

sed 'H;1h;$!d;x;s/\(.*\),/\1 and/'

Обратите внимание, что такая обработка файла не рекомендуется для очень больших файлов, так как при этом будет использоваться много оперативной памяти.

2
27.01.2020, 22:16

sed предназначен для выполнения простого s/old/new для отдельных строк, вот и все. Почти каждый раз, когда вы обнаруживаете, что используете конструкции, отличные от s, g и p (с -n ), и, конечно же, каждый раз, когда вы ловите себя на том, что говорите о «задержке пространства», вы используете неправильный инструмент. Для чего-то более сложного, чем s/old/new, как эта задача, вы должны просто использовать вместо этого awk. Следующее будет работать с использованием любого awk в любой оболочке на любом компьютере UNIX, не сохраняет весь файл в памяти и его легко настроить, если/когда вы хотите дополнительно сделать что-нибудь еще с текстом:

$ cat tst.awk
/,/ { printf "%s", prev; prev="" }
{ prev = prev $0 ORS }
END {
    if ( match(prev,/.*,/) ) {
        prev = substr(prev,1,RLENGTH-1) " and" substr(prev,RLENGTH+1)
    }
    printf "%s", prev
}

$ awk -f tst.awk file
yellow, green,
blue, black, purple,
orange,
white, red and brown
are some colours

Вы МОЖЕТЕ сделать эту работу короче в awk, запихнув весь файл в память и написав эту загадочную руну:

$ awk '{r=r$0 ORS} END{h=r;sub(/,[^,]+$/,"",h);sub(/.*,/,"",r);printf "%s and%s",h,r}' file
yellow, green,
blue, black, purple,
orange,
white, red and brown
are some colours

но дело в том, что, в отличие от sed, вам это не нужно.

0
27.01.2020, 22:16

Теги

Похожие вопросы