2013-03-16 4 views
1

Прежде всего, я не программист - просто пытаюсь изучить основы сценариев оболочки и попробовать некоторые вещи.Создать каталог, основанный на части имени файла

Я пытаюсь создать функцию для моего скрипта bash, которая создает каталог на основе номера версии в имени файла, выбранного пользователем в списке.

Вот функция:

lav_mappe() { 

shopt -s failglob 
echo "[--- Choose zip file, or x to exit ---]" 
echo "" 
echo "" 

select zip in $SRC/*.zip 
do 
[[ $REPLY == x ]] && . $HJEM/build 
[[ -z $zip ]] && echo "Invalid choice" && continue 
echo 
    grep ^[0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}$ $zip; mkdir -p $MODS/out/${ver} 
done 
} 

Я пытался возиться с некоторыми другими командами тоже:

for ver in $zip; do 
grep "^[0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}$" $zip; mkdir -p $MODS/out/${ver} 
done 

А также find | grep - но я делаю это неправильно :(

Но это заканчивается тем, что «не соответствует» для моего шаблона регулярных выражений.

Я пытаюсь взять имя файла, которое выбрал пользователь, затем grep его для номера версии (ALWAYS x.xx.x где-то в имени файла) и fianlly создать каталог именно с этим.

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

EDIT:

Ok, это то, как полная функция выглядит сейчас: (Пожалуйста, обратите внимание, что СЭД (1) команды, кроме создания каталогов не создается мной, просто реализован в моем коде.)

Pastebin (Long code.)

ответ

2

у меня есть новости для вас. Вы пишете скрипт Bash, вы являются программистом!

Ваше регулярное выражение (RE) относится к «неправильному» типу. Vanilla grep использует форму, известную как «Основные регулярные выражения» (BRE), но ваш RE имеет форму расширенного регулярного выражения (ERE). BRE, используются по ванили grep, vi, more и т.д. EREs используется почти все остальное, awk, Perl, Python, Java, .Net и т.д. Проблема заключается в том, вы пытаетесь искать этот шаблон в содержимом файл, не в имени файла!

Существует egrep команда, или вы можете использовать grep -E, так:

echo $zip|grep -E '^[0-9]\.[0-9]{1,2}\.[0-9]{1,2}$' 

(обратите внимание, что одиночные кавычки являются более безопасными, чем в два раза). Кстати, вы используете ^ спереди и $ в конце, что означает, что имя файла ТОЛЬКО состоит из номера версии, но вы говорите, что номер версии «где-то в имени файла». Это не значит, что вам нужен квантификатор .

НО, вы, похоже, не забираете номер версии.

Вы можете использовать sed (мы также нуждаемся в -E):

ver=$(echo $zip| sed -E 's/.*([0-9]\.[0-9]{1,2}\.[0-9]{1,2}).*/\1/') 

\1 на праве означает «заменить все с тем, что было подобрано в (именно поэтому мы имеем * спереди и сзади). круглые скобки ". Это немного неуклюже, я знаю.

Теперь мы можем сделать mkdir (не заслуга в класть все на одной линии, и это делает код труднее поддерживать):

mkdir -p "$MODS/out/$ver" 

${ver} ненужно в этом случае, но это хорошая идея заключить имена путей в двойные кавычки, если любой из компонентов имеет встроенное белое пространство.

Итак, хорошее усилие для «не-программиста», особенно при создании этого RE.

Теперь Урок 2

Будьте осторожны при использовании этого решения в общем цикле. В вашем конкретном вопросе используется select, поэтому мы не можем предсказать, какие файлы будут использоваться. Но что, если мы хотим сделать это за каждый файл?

Использование вышеуказанного решения в цикле for или while будет неэффективным. Вызов внешних процессов внутри цикла всегда плох. Мы ничего не можем сделать с mkdir без использования другого языка, такого как Perl или Python. Но sed, по своей природе итеративно, и мы должны использовать эту функцию.

Одним из вариантов было бы использовать шаблон раковины, соответствующий вместо sed. Эта конкретная модель не была бы невозможна в оболочке, но это было бы сложно и вызвать другие вопросы. Поэтому давайте придерживаться sed.

Проблема заключается в том, что echo выход помещает пространство между каждым полем. Это дает нам пару вопросов. sed ограничивает каждую запись новой строкой «\ n», поэтому echo сам по себе здесь не будет. Мы могли бы заменить каждое пространство новой строкой, но это было бы проблемой, если бы были пробелы внутри имени файла. Мы могли бы сделать некоторые обманки с IFS и подталкиванием, но это приводит к ненужным осложнениям. Поэтому вместо этого мы вернемся к добрым старым ls. Как правило, мы не хотели бы использовать ls, более гибкое чередование оболочки, но здесь мы используем функцию, которая будет размещать новую строку после каждого имени файла (при использовании перенаправленного через канал).

while read ver 
do 
    mkdir "$ver" 
done < <(ls $SRC/*.zip|sed -E 's/.*([0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}).*/\1/') 

Здесь я использую процесс замены, и этот цикл будет вызывать только ls и sed один раз. НО, он вызывает программу mkdirn раз.

Lession 3

Извините, но это по-прежнему неэффективны. Мы создаем дочерний процесс для каждой итерации, для создания каталога требуется только один вызов API ядра, но мы создаем для этого только процесс?Давайте используем более сложный язык, такой как Perl:

#!/usr/bin/perl 

use warnings; 
use strict; 

my $SRC = '.'; 

for my $file (glob("$SRC/*.zip")) 
{ 
    $file =~ s/.*([0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}).*/$1/; 
    mkdir $file or die "Unable to create $file; $!"; 
} 

Возможно, вы заметили, что ваш RE прошел здесь! Но теперь у нас больше контроля, и никакие дочерние процессы (mkdir в Perl не являются встроенными, как и glob).

В заключение, для небольшого количества файлов петля sed выше будет в порядке. Это просто и оболочка. Вызов Perl только для этого из сценария, вероятно, будет медленнее, так как perl довольно большой. Но скрипты оболочки, которые создают дочерние процессы внутри циклов, не масштабируются. Perl есть.

+0

Aah! После всех моих усилий, я вижу, что мне не хватает самой простой вещи, «echo $ zip», ха-ха! Изучайте каждый день! Я вижу с помощью команды SED, это меняет мое представление о том, как искать строки в файлах, а не использовать grep :) Теперь я напишу свою функцию и закончу код, я отправлю его здесь, чтобы показать результаты! – user1170663

+0

@ user1170663: точка в том, что 'grep' ищет строки в файлах, но это не то, что вы хотели сделать, вы хотели * извлечь * строку из имени файла. Другой. – cdarke

+0

@ user1170663: см. Мои новые изменения. Я боюсь, что боюсь! – cdarke

Смежные вопросы