Bash и имена файлов с пробелами

@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

25
задан Peter Mortensen 27 March 2012 в 15:15
поделиться

4 ответа

Попробуйте следующее:

(IFS=$'\n'; grep -li 'regex' $(<listOfFiles.txt))

IFS - это внутренний разделитель полей. Установка его на $ '\ n' указывает Bash использовать символ новой строки для разделения имен файлов. Его значение по умолчанию $ '\ t \ n' и может быть напечатано с помощью cat -etv <<< "$ IFS" .

Заключение скрипта в скобки запускает подоболочку так что пользовательское значение IFS влияет только на команды в скобках.

44
ответ дан 28 November 2019 в 18:31
поделиться

Это работает:

while read file; do grep -li dtw "$file"; done < listOfFiles.txt
6
ответ дан 28 November 2019 в 18:31
поделиться
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; однако это может стать проблемой для других.

8
ответ дан 28 November 2019 в 18:31
поделиться

Это моё любимое решение, хотя оно может превзойти совпадение:

grep -i 'regex' $(cat listOfFiles.txt | sed -e "s/ /?/g")
0
ответ дан 28 November 2019 в 18:31
поделиться
Другие вопросы по тегам:

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