Метод WatchService WatchEvent .context() возвращает несогласованный относительный путь к файлу в ENTRY_MODIFY (goutputstream-####, ОС Linux)

В этом коде я надеюсь обновить HashMap самой последней версией содержимого заданного пути, при этом абсолютный путь в виде строки используется в качестве ключа. Проблема в том, что метод .context() WatchEvent дает мне разные относительные пути для одного и того же файла для каждого события.

Вот фрагмент кода:

            else if(event.kind()==StandardWatchEventKinds.ENTRY_MODIFY)
            {
                /*Variable path is a Path of "//workspaces", set earlier.*/
                Path oldfilepath=path.resolve((Path)event.context()); /*problem line*/
                String oldfilepathstring = oldfilepath.toString();
                 FileReader oldIn = new FileReader(oldfilepathstring);
                 BufferedReader br = new BufferedReader(oldIn);
                 String line;
                 List<String> newfiletext=new LinkedList<>();
                  while((line = br.readLine())!=null)
                    newfiletext.add(line);

                 List<String> previousText=new LinkedList<>();
                 if((previousText = fileMappings.get(oldfilepathstring))!= null)
                 {
                      System.out.println("previoustext:\n"+previousText);
                      System.out.println("newfiletext:\n"+newfiletext);
                 }

                 fileMappings.put(oldfilepathstring, newfiletext);
                 System.out.println(fileMappings.keySet()+"\n"+fileMappings.values());
            }

        }

А вот пример вывода при изменении файла b.txt в отслеживаемом каталоге с содержимого "abc" на "abc 123"

Обратите внимание, что все это можно получить, просто открыв файл /workspaces/b.txt (который уже существует) и изменив его содержимое.):


    run:
    ENTRY_CREATE:.goutputstream-BRC1HX
    ENTRY_MODIFY:.goutputstream-BRC1HX
    [/workspaces/.goutputstream-BRC1HX]
    [[]]
    ENTRY_MODIFY:.goutputstream-BRC1HX
    previoustext:
    []
    newfiletext:
    [abc]
    [/workspaces/.goutputstream-BRC1HX]
    [[abc]]
    ENTRY_CREATE:b.txt~
    ENTRY_CREATE:b.txt
    ENTRY_CREATE:.goutputstream-MFJ6HX
    ENTRY_MODIFY:.goutputstream-MFJ6HX
    [/workspaces/.goutputstream-MFJ6HX, /workspaces/.goutputstream-BRC1HX]
    [[], [abc]]
    ENTRY_MODIFY:.goutputstream-MFJ6HX
    previoustext:
    []
    newfiletext:
    [abc, 123]
    [/workspaces/.goutputstream-MFJ6HX, /workspaces/.goutputstream-BRC1HX]
    [[abc, 123], [abc]]
    ENTRY_CREATE:b.txt~
    ENTRY_CREATE:b.txt

Интересующая линия Path oldfilepath=path.resolve((Path)event.context());

Обратите внимание, как oldfilepath преобразовался в «/workspaces/.goutputstream-MFJ6HX», а затем в «/workspaces/.goutputstream-BRC1HX» для того же файла.

event.context() возвращает другой путь для одного и того же файла после каждой модификации. Является ли это проблемой Linux или проблемой Java, и как именно мне получить стандартный относительный путь (в данном случае это будет «b.txt») для этого файла?

Кажется, что когда я выполняю изменение, я получаю последовательность событий создания/изменения/создания, и ENTRY_CREATEs имеют правильное имя файла, а ENTRY_MODIFYs имеют временный дескриптор (я предполагаю, что временная версия файла используется между сохранениями.) Мне нужно иметь возможность фиксировать изменение файла и извлекать правильное имя файла из этого события.

Я понимаю, что моя файловая система может создавать и обрабатывать временные файлы под капотом, в то время как я просто открываю, изменяю и сохраняю файл, но как именно мне извлечь правильное имя файла из временного файла, который дает мне событие, указывающее ENTRY_MODIFY? Есть ли какой-то способ сгруппировать события, относящиеся к этому изменению, чтобы я мог просто найти закрывающий ENTRY_CREATE и получить из него имя файла? Или как-то пройти вверх по стеку вызовов, ведущих к этому ENTRY_CREATE?

Я вижу имя файла во вложенных событиях ENTRY_CREATE, окружающих каждое ENTRY_MODIFY, но я надеюсь, что есть более элегантный способ сделать это, чем как-то (получить самое последнее событие, которое не было ENTRY_MODIFY, а затем получить .context() из него. )

Спасибо!


person Mer    schedule 19.06.2014    source источник


Ответы (2)


Я столкнулся с тем же вопросом. Я думаю, что это не имеет ничего общего с какой-либо проблемой Linux или проблемами Java. Это именно то, как редактор b.txt (я полагаю, gedit) справляется с этим.

После сохранения

  1. создает новый временный файл ".goutputstream-xxxx" со случайным xxxx (создание, которое вы видите),

  2. записывает новое содержимое в этот файл (изменение, которое вы видите),

  3. переименовывает исходный файл в b.txt~ (создание, которое вы видите),

  4. и переименовывает временный файл в b.txt (создание, которое вы видите)

Поэтому я думаю, вам нужно следить за ENTRY_MODIFY и ENTRY_CREATE в отношении b.txt, чтобы действительно увидеть все модификации файла.

person Christian M. Schmid    schedule 21.06.2014
comment
Спасибо. Меня удивляет, что Java создала библиотеку, которая зависит от системного текстового редактора по умолчанию. Я бы подумал, что проблема межсистемной переносимости исключила бы такие функции. - person Mer; 22.06.2014

У меня такая же проблема. Я использую фильтр, чтобы исключить эту проблему. Например: мне нужны все файлы по пути /home/user/data/in;

Files.walk(Paths.get( System.getProperty("user.home").concat("/data/in")))
                    .map(Path::toString)
                    .filter(path -> ! path.endsWith("/in"))
                    .filter(path -> ! path.startsWith(".goutputstream"))
                    .collect(Collectors.toList());

Это исправление для меня.

person Rafael Schneider    schedule 12.08.2020