сценарии оболочки: поиск / замена и проверка файла существуют - PullRequest
0 голосов
/ 17 апреля 2010

У меня есть Perl-скрипт (или любой исполняемый файл) E, который возьмет файл foo.xml и напишет файл foo.txt. Я использую кластер Beowulf для запуска E для большого количества XML-файлов, но я хотел бы написать простой сценарий сервера заданий в оболочке (bash), который не перезаписывает существующие текстовые файлы.

Я сейчас делаю что-то вроде

#!/bin/sh
PATTERN="[A-Z]*0[1-2][a-j]"; # this matches foo in all cases 
todo=`ls *.xml | grep $PATTERN -o`;
isdone=`ls *.txt | grep $PATTERN -o`;

whatsleft=todo - isdone; # what's the unix magic?

#tack on the .xml prefix with sed or something

#and then call the job server; 
jobserve E "$whatsleft";

и тогда я не знаю, как получить разницу между $ todo и $ isdone. Я бы предпочел использовать sort / uniq чему-то вроде цикла for с grep внутри, но я не уверен, как это сделать (каналы? Временные файлы?)

В качестве дополнительного вопроса, есть ли способ выполнить поиск в bash grep?

Для выяснения / расширения проблемы:

У меня есть несколько программ, которые получают данные из таких источников, как (но не обязательно), data / {branch} / special / {pattern} .xml и записывают выходные данные в другой каталог результатов / special / {branch} - {pattern} .txt (или данные / {ветвь} / промежуточный / {шаблон} .dat, например). Я хочу проверить в своем скрипте для поиска работы, существует ли этот файл.

Таким образом, E преобразует данные / {branch} / special / {pattern} .xml-> results / special / {branch} - {pattern} .dat, например. Я хочу посмотреть на каждый экземпляр ввода и проверить, существует ли выход. Один (по общему признанию более простой) способ сделать это - просто прикоснуться к файлам * .done рядом с каждым входным файлом и проверить эти результаты, но я бы предпочел не управлять ими, и иногда задания завершаются ненадлежащим образом, поэтому я их не хочу помечено сделано.

N.B. Мне пока не нужно проверять параллелизм или блокировать какие-либо файлы.

Таким образом, простой и понятный способ решения вышеуказанной проблемы (в псевдокоде) может быть

for i in `/bin/ls *.xml`
do
   replace xml suffix with txt
   if [that file exists]
      add to whatsleft list
   end
done

но я ищу что-то более общее.

Ответы [ 5 ]

1 голос
/ 17 апреля 2010
#!/bin/sh

shopt -s extglob # allow extended glob syntax, for matching the filenames

LC_COLLATE=C     # use a sort order comm is happy with

IFS=$'\n'        # so filenames can have spaces but not newlines
                 # (newlines don't work so well with comm anyhow;
                 # shame it doesn't have an option for null-separated
                 # input lines).

files_todo=( **([A-Z])0[1-2][a-j]*.xml )
files_done=( **([A-Z])0[1-2][a-j]*.txt )
files_remaining=( \
  $(comm -23 --nocheck-order \
    <(printf "%s\n" "${files_todo[@]%.xml}") \
    <(printf "%s\n" "${files_done[@]%.txt}") ))

echo jobserve E $(for f in "${files_remaining[@]%.xml}"; do printf "%s\n" "${f}.txt"; done)

Предполагается, что вы хотите один jobserve E вызов со всеми оставшимися файлами в качестве аргументов; из спецификации довольно непонятно, так ли это.

Обратите внимание на использование расширенных шаров вместо разбора ls, что считается очень плохой практикой .

Чтобы преобразовать входные данные в выходные имена, не используя ничего, кроме встроенных в оболочку, учтите следующее:

if [[ $in_name =~ data/([^/]+)/special/([^/]+).xml ]] ; then
  out_name=results/special/${BASH_REMATCH[1]}-${BASH_REMATCH[2]}.dat
else
  : # ...handle here the fact that you have a noncompliant name...
fi
1 голос
/ 17 апреля 2010

Название вопроса предполагает, что вы можете искать:

 set -o noclobber

Содержание вопроса указывает на совершенно другую проблему!

Кажется, вы хотите запустить 'jobserve E' для каждого файла '.xml' без соответствующего файла '.txt'. Вам необходимо оценить проблемы TOCTOU (время проверки, время использования), потому что вы находитесь в кластерной среде. Но основная идея может быть:

 todo=""
 for file in *.xml
 do [ -f ${file%.xml}.txt ] || todo="$todo $file"
 done
 jobserve E $todo

Это будет работать как с оболочкой Korn, так и с Bash. В Bash вы можете изучить превращение «todo» в массив; это будет иметь дело с пробелами в именах файлов лучше, чем это.

Если у вас есть процессы, все еще генерирующие файлы «.txt» для файлов «.xml» во время выполнения этой проверки, вы получите некоторое дублирующее усилие (поскольку этот скрипт не может сказать, что обработка происходит). Если процесс «E» создает соответствующий файл «.txt», когда он начинает его обрабатывать, это минимизирует вероятность или дублирующее усилие. Или, возможно, рассмотрите возможность отделения обработанных файлов от необработанных файлов, поэтому процесс 'E' перемещает файл .xml из каталога 'to-be-done' в каталог 'done' (и записывает '.txt' файл в каталог 'done' тоже). Если все сделано аккуратно, это поможет избежать большинства проблем с многократной обработкой. Например, вы можете связать «.xml» с каталогом «done» при запуске обработки и обеспечить соответствующую очистку с помощью обработчика «atexit ()» (если вы уверены, что ваша программа обработки не дает сбоя). Или другой трюк с вашим собственным изобретением.

1 голос
/ 17 апреля 2010
whatsleft=$( ls *.xml *.txt | grep $PATTERN -o | sort | uniq -u )

Обратите внимание, что на самом деле получается симметричная разница.

0 голосов
/ 17 апреля 2010

для потомков, вот что я нашел для работы:

TMPA='neverwritethis.tmp'
TMPB='neverwritethat.tmp'
ls *.xml | grep $PATTERN -o > $TMPA;
ls *.txt | grep $PATTERN -o > $TMPB;
whatsleft = `sort $TMPA $TMPB | uniq -u | sed "s/%/.xml" > xargs`;
rm $TMPA $TMPB;
0 голосов
/ 17 апреля 2010

Я не совсем уверен, что вы хотите, но вы можете сначала проверить наличие файла, если он существует, создать новое имя? (Или в вашем E (Perl-скрипт) вы делаете эту проверку.)

if [ -f "$file" ];then
  newname="...."
fi
...
jobserve E .... > $newname 

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

...