2010-04-16 4 views
0

У меня есть скрипт perl (или любой исполняемый файл) E, который возьмет файл foo.xml и напишет файл foo.txt. Я использую кластер Beowulf для запуска E для большого количества XML-файлов, но я бы хотел написать простой сценарий сервера заданий в shell (bash), который не перезаписывает существующие файлы txt.shell scripting: search/replace & check file существуют

В настоящее время я делаю что-то вроде

#!/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?

Чтобы уточнить/расширить эту проблему:

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

Таким образом, 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 

, но я искал что-то более общее.

+0

'txtfile = $ {XMLFILE% .xml} .txt' делает замену - как в моем ответе. –

+0

Когда вы говорите «во избежание перезаписи файлов» - нужно ли нам быть совместимым с параллелизмом? Если это так, нам нужно сделать некоторую блокировку. (Если это так ... мы находимся в общей файловой системе? Какой из них имеет правильную семантику для 'flock'?) –

+0

нет понимания параллелизма, без блокировки - это общая файловая система, но это побочный проект на данный момент – johndashen

ответ

1
#!/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, то есть considered very poor practice.

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

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 
+0

это выглядит великолепно. Я не знал об IFS или комм. Можете ли вы объяснить, что делают линии shopt и LC_COLLATE? – johndashen

+0

Линия 'shopt' устанавливает флаг' extglob', который позволяет нам сопоставлять файлы с помощью расширенного синтаксиса glob (эффективно, что я делаю, чтобы соответствовать только соответствующим файлам без регулярного выражения). 'LC_COLLATE = C' устанавливает порядок сортировки по умолчанию (в данном случае для файлов globbed) для чего-то, что' comm' будет доволен. –

+0

Хороший вопрос о 'ls'. Хотя я думаю, что заменить его на 'find' было бы намного проще и читаем здесь. – slacker

0

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

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

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

+0

это поведение, которое я хочу, но я не хочу рассчитывать на скрипт/исполняемый файл perl, чтобы предотвратить перезапись. – johndashen

1

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

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») файл в «готовый» каталог тоже). Если сделать это осторожно, это может избежать большинства проблем с многопроцессорной обработкой. Например, вы можете связать «.xml» с каталогом «done» при запуске обработки и обеспечить соответствующую очистку обработчиком atexit() (если вы уверены, что ваша программа обработки не сбой). Или другой трюк вашего собственного дизайна.

+0

это будет работать для меня, так как сценарий E не будет получать доступ к перекрывающимся файлам между вызовами. У меня есть несколько последующих вопросов, так как я довольно новичок в bash-скриптах: (1) Можно ли использовать glob с несколькими звездочками в разделе for-in? как в \ */special/\ *. xml? (2) ли синтаксис% удаляет все экземпляры .xml? – johndashen

+0

(1) Да; (2) Нет. Единственный% удаляет только последний «.xml» (поэтому x.xml.xml.xml -> x.xml.xml). –

1
whatsleft=$(ls *.xml *.txt | grep $PATTERN -o | sort | uniq -u) 

Примечания это на самом деле получает симметричной разницы.

+0

это будет работать для меня в этом примере, но я немного упростил его: я хотел бы сделать эту работу для разных шаблонов, например, из * .xml -> * -reordered.xml, а также для каталогов. в этом случае я использовал ls с --ignore: можете ли вы изменить свою команду, чтобы это учесть? – johndashen

+0

@johndashen: Я не понимаю, почему это не сработает, или, может быть, я просто не понимаю, что вы имеете в виду :). Не могли бы вы объяснить более четко, желательно с примером? – slacker

+0

, если я заменю * .txt в вашем примере на * -reordered.xml, я всегда получаю копию * -reordered.xml дважды ... но uniq позаботится об этом, поэтому на самом деле это не проблема. да. =) – johndashen

0

ради потомства, это то, что я нашел работу:

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

Было бы здорово, если бы $ TMPA и $ TMPB на самом деле назывались трубами. – slacker

+0

См. Ответ, который я дал, который не требует временных файлов, и использует только одну внешнюю команду ('comm'), а не там (' sort', 'uniq' и' sed'). –