@dcramer предложил более элегантное (на мой взгляд) решение этой проблемы.
https://gist.github.com/730765
from django.db.models.signals import post_init
def track_data(*fields):
"""
Tracks property changes on a model instance.
The changed list of properties is refreshed on model initialization
and save.
>>> @track_data('name')
>>> class Post(models.Model):
>>> name = models.CharField(...)
>>>
>>> @classmethod
>>> def post_save(cls, sender, instance, created, **kwargs):
>>> if instance.has_changed('name'):
>>> print "Hooray!"
"""
UNSAVED = dict()
def _store(self):
"Updates a local copy of attributes values"
if self.id:
self.__data = dict((f, getattr(self, f)) for f in fields)
else:
self.__data = UNSAVED
def inner(cls):
# contains a local copy of the previous values of attributes
cls.__data = {}
def has_changed(self, field):
"Returns ``True`` if ``field`` has changed since initialization."
if self.__data is UNSAVED:
return False
return self.__data.get(field) != getattr(self, field)
cls.has_changed = has_changed
def old_value(self, field):
"Returns the previous value of ``field``"
return self.__data.get(field)
cls.old_value = old_value
def whats_changed(self):
"Returns a list of changed attributes."
changed = {}
if self.__data is UNSAVED:
return changed
for k, v in self.__data.iteritems():
if v != getattr(self, k):
changed[k] = v
return changed
cls.whats_changed = whats_changed
# Ensure we are updating local attributes on model init
def _post_init(sender, instance, **kwargs):
_store(instance)
post_init.connect(_post_init, sender=cls, weak=False)
# Ensure we are updating local attributes on model save
def save(self, *args, **kwargs):
save._original(self, *args, **kwargs)
_store(self)
save._original = cls.save
cls.save = save
return cls
return inner
Попробуйте следующее:
(IFS=$'\n'; grep -li 'regex' $(<listOfFiles.txt))
IFS
- это внутренний разделитель полей. Установка его на $ '\ n'
указывает Bash использовать символ новой строки для разделения имен файлов. Его значение по умолчанию $ '\ t \ n'
и может быть напечатано с помощью cat -etv <<< "$ IFS"
.
Заключение скрипта в скобки запускает подоболочку так что пользовательское значение IFS
влияет только на команды в скобках.
Это работает:
while read file; do grep -li dtw "$file"; done < listOfFiles.txt
cat listOfFiles.txt |tr '\n' '\0' |xargs -0 grep -li 'regex'
Параметр -0 в xargs указывает xargs использовать нулевой символ, а не пробел в качестве признака конца имени файла. Команда tr преобразует входящие символы новой строки в нулевой символ.
Это соответствует требованию OP о том, что grep нельзя вызывать несколько раз. По моему опыту, для большого количества файлов избегание многократных вызовов grep значительно улучшает производительность.
Эта схема также позволяет избежать ошибки в исходном методе OP, потому что его схема не работает там, где listOfFiles.txt содержит несколько файлов это превысит размер буфера для команд. xargs знает о максимальном размере команды и будет вызывать grep несколько раз, чтобы избежать этой проблемы.
Связанная проблема с использованием xargs и grep заключается в том, что grep будет префикс вывода с именем файла при вызове с несколькими файлами. Поскольку xargs вызывает grep с несколькими файлами, один будет получать вывод с префиксом имени файла, но не в случае одного файла в listOfFiles.txt или в случае нескольких вызовов, когда последний вызов содержит одно имя файла. Чтобы добиться согласованного вывода, добавьте / dev / null к команде grep:
cat listOfFiles.txt |tr '\n' '\0' |xargs -0 grep -i 'regex' /dev/null
Обратите внимание, что это не было проблемой для OP, поскольку он использовал параметр -l для grep; однако это может стать проблемой для других.
Это моё любимое решение, хотя оно может превзойти совпадение:
grep -i 'regex' $(cat listOfFiles.txt | sed -e "s/ /?/g")