Лучший способ изменить файл при использовании каналов?

У меня часто есть задачи программирования оболочки, где я сталкиваюсь с этим шаблоном:

cat file | some_script > file

Это небезопасно - кошка не могла читать во всем файле, прежде чем some_script начнет писать в него. Я действительно не хочу писать результат во временный файл (его медленное, и я не хочу добавленной сложности продумывания уникального нового имени).

Возможно, существует существует стандартная команда оболочки, которая буферизует целый поток, пока EOF не будет достигнут? Что-то как:

cat file | bufferUntilEOF | script > file

Идеи?

9
задан user48956 18 January 2010 в 23:07
поделиться

6 ответов

Вы ищете губку .

4
ответ дан 4 December 2019 в 12:18
поделиться

Здесь использование временного файла - правильное решение. Когда вы используете перенаправление типа '>', оно обрабатывается оболочкой, и независимо от того, сколько команд в вашей цепочке, оболочка может удалять и перезаписывать выходной файл перед выполнением любой команды (во время настройки цепочек).

5
ответ дан 4 December 2019 в 12:18
поделиться

Проходя через различные ответы здесь, и сероголовые соколы обновления и разъяснения, кажется, что то, что хочет asker является класс, который выглядит, пахнет и звучит как Последовательность, но не является.

То есть они хотели бы иметь возможность:

MyString string = "String!!";

Это не может работать, так как java.lang. Последовательность является конечным классом, и поэтому каждый «Последовательности» , который создает компилятор, будет объектом java.lang. Последовательность , так как это не объект MyString , они не могут быть назначены друг другу.

В слабо набранных языках вы смогли бы создать такой класс, так как если класс выглядит, пахнет и звучит как утка , то по всем замыслам и целям, это утка. Ява, однако, является сильно типизированным языком, а утка - только утка, если это происходит из семейства птиц Anatidae .

-121--3224995-

Единственное, что я вижу, это то, что вы получаете доступ к q одновременно из разных потоков - без блокировки, как вы действительно пишете. Это требует неприятностей - и вы, вероятно, получаете неприятности в виде переводчика Python, запирающегося на вас.:

Попробуйте заблокировать, это действительно не очень сложно:

import sys, time
import xml.parsers.expat
import threading

q = []
q_lock = threading.Lock() <---

def start_handler(name, attrs):
    q_lock.acquire() <---
    q.append(name)
    q_lock.release() <---

def do_expat():
    p = xml.parsers.expat.ParserCreate()
    p.StartElementHandler = start_handler
    p.buffer_text = True
    print("opening {0}".format(sys.argv[1]))
    with open(sys.argv[1]) as f:
        print("file is open")
        p.ParseFile(f)
        print("parsing complete")


t = threading.Thread(group=None, target=do_expat)
t.start()

while True:
    q_lock.acquire() <---
    print(q)
    q_lock.release() <---
    time.sleep(1)

Вы видите, это было действительно просто, мы просто создали блокировочную переменную, чтобы защитить наш объект, и получить эту блокировку каждый раз, прежде чем мы используем объект и освободить каждый раз после того, как мы завершили нашу задачу на объекте. Таким образом мы гарантировали, что q.append (имя) никогда не будет перекрываться с print (q) .


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

-121--4196160-

Как и многие другие, я люблю использовать временные файлы. Я использую идентификатор процесса оболочки как часть временного имени, чтобы, если одновременно выполняется несколько копий сценария, они не конфликтовали. Наконец, я тогда только перезаписать исходный файл, если сценарий успешно (с помощью логического оператора короткого замыкания - это немного плотный, но очень приятно для простых командных строк). Сложение всего этого вместе выглядит следующим образом:

some_script < file > smscrpt.$$ && mv smscrpt.$$ file

При сбое команды временный файл будет оставлен. Если вы хотите очистить от ошибки, вы можете изменить это на:

some_script < file > smscrpt.$$ && mv smscrpt.$$ file || rm smscrpt.$$

BTW, я избавился от плохого использования кошки и заменил ее на перенаправление ввода.

3
ответ дан 4 December 2019 в 12:18
поделиться

Использование временного файла - это IMO лучше, чем пытаясь буферными данными в трубопроводе.

Это почти побеждает цель трубопроводов для их буфера.

1
ответ дан 4 December 2019 в 12:18
поделиться

Я думаю, что лучший способ - использовать временный файл. Однако, если вам нужен другой подход, вы можете использовать что-то вроде awk для буферизации входных данных в памяти до того, как ваше приложение начнет получать входные данные. Следующий скрипт буферизирует все входные данные в массиве строк до того, как начнёт выводить их следующему потребителю.

{ lines[NR] = $0; }
END {
    for (line_no=1; line_no<=NR; ++line_no) {
        print lines[line_no];
    }
}

Вы можете свернуть его в одну строку, если хотите:

cat file | awk '{lines[NR]=$0;} END {for(i=1;i<=NR;++i) print lines[i];}' > file

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

1
ответ дан 4 December 2019 в 12:18
поделиться

Использование MKTEMP (1) или Tempfile (1) Сохраняет вам счет необходимости придумать уникальное имя файла.

2
ответ дан 4 December 2019 в 12:18
поделиться