Создание переменной по имени (shell)
Модератор: Модераторы разделов
-
LXj
- Сообщения: 94
Создание переменной по имени
Есть у меня переменная $LINE, ну, к примеру, содержащая строку "extract='unzip'" или "extract='tar -xjf'"
Чтобы мне такого сделать с этой переменной, чтобы появилась новая переменная $extract, содержащая соответственно то значение, которое в $LINE справа от присвоения?
То есть такой эффект я ожидал от `$LINE`, но оно не помогло
Чтобы мне такого сделать с этой переменной, чтобы появилась новая переменная $extract, содержащая соответственно то значение, которое в $LINE справа от присвоения?
То есть такой эффект я ожидал от `$LINE`, но оно не помогло
-
KiWi
- Бывший модератор
- Сообщения: 2521
- Статус: статус, статус, статус
Re: Создание переменной по имени
Код: Выделить всё
export $LINE-
LXj
- Сообщения: 94
Re: Создание переменной по имени
Хм, что же я такого делал, что у меня с export никакой вариант не работал? Кажется, где-то у меня была опечатка
В общем, правильный ответ:
Спасибо за помощь!
В общем, правильный ответ:
Код: Выделить всё
export "$LINE"Спасибо за помощь!
-
LXj
- Сообщения: 94
Re: Создание переменной по имени
Ага.
Не всё так просто
Вот как это происходит в консоли:
Поэтому я написал, что правильнее export "$LINE". Проблема в том, что в скрипте это не работает
Вот он скрипт:
Запускаю:
То есть переменные, которые должны были экспортироваться, остались без значений!
PS. На самом деле скрипт должен заканчиваться примерно так:
... но должны быть ещё проверка ошибок и другие фичи
Update Подгулявшее форматирование исправил
Странная вещь...
ALL_EXTENSIONS должна в конце цикла (если выходим не по break) содержать все возможные расширения (для вывода хелпа)
Если в цикл добавить вывод значений ALL_EXTENSIONS, то мы видим, как эти расширения дописываются в переменную
Если же вывод поставить ПОСЛЕ цикла, то переменная также окажется пустой!
Не всё так просто
Вот как это происходит в консоли:
Код: Выделить всё
lx@LX ~/bin $ echo "$LINE"
extract='tar -xjf'
lx@LX ~/bin $ export $LINE
bash: export: `-xjf'': not a valid identifier
lx@LX ~/bin $ export "$LINE"
lx@LX ~/bin $ echo "$extract"
'tar -xjf'Поэтому я написал, что правильнее export "$LINE". Проблема в том, что в скрипте это не работает
Вот он скрипт:
Код: Выделить всё
#!/bin/sh
## BAH: Beautiful archiving helper
## Generated by LXj, do not modify;)
SIMPLE_ACTIONS="extract list"
FORMATS="
.tar.gz
.tar.Z
.tgz
extract='tar -xzf'
list='tar -lzf'
.tar.bz2
.tbz2
extract='tar -xjf'
list='tar -ljf'
.zip
extract='unzip'
list='unzip -l'
"
ALL_EXTENSIONS=""
EXTENSION=""
echo "$FORMATS" | while read LINE
do
ACTION=`echo $LINE | grep "="`
if [ -z "$LINE" ]
then
if [ -n "$EXTENSION" ]
then
break
fi
elif [ -z "$ACTION" ]
then
ALL_EXTENSIONS="$ALL_EXTENSIONS $LINE"
EXTENSION=`echo $2 | grep "$LINE"`
elif [ -n "$EXTENSION" ]
then
export "$LINE" # без кавычек будет такая же ошибка, как в примере вверху
fi
done
echo $extract
echo $listЗапускаю:
Код: Выделить всё
lx@LX ~/opt/writer2latex04 $ bah list w2lfilters.zipТо есть переменные, которые должны были экспортироваться, остались без значений!
PS. На самом деле скрипт должен заканчиваться примерно так:
Код: Выделить всё
`eval "\$$1"` $2... но должны быть ещё проверка ошибок и другие фичи
Update Подгулявшее форматирование исправил
Странная вещь...
ALL_EXTENSIONS должна в конце цикла (если выходим не по break) содержать все возможные расширения (для вывода хелпа)
Если в цикл добавить вывод значений ALL_EXTENSIONS, то мы видим, как эти расширения дописываются в переменную
Если же вывод поставить ПОСЛЕ цикла, то переменная также окажется пустой!
-
KiWi
- Бывший модератор
- Сообщения: 2521
- Статус: статус, статус, статус
Re: Создание переменной по имени
Вы, батенька, извращенец.
Дарю.
При небольших изменениях должен и не под bash работать... правда, не помню, что именно реализует array.
Код: Выделить всё
#!/bin/bash
FORMATS=(
"\.tar\.gz \.tar\.Z \.tgz"
"\.tar\.bz2 \.tbz2"
)
EXTRACT=(
"tar -xzf"
"tar -xjf"
)
LIST=(
"tar -lzf"
"tar -ljf"
)
FILETYPE=""
i=0
while [[ $i -lt ${#FORMATS[@]} && -z $FILETYPE ]]; do
for filetype in ${FORMATS[$i]}; do
[ `expr match "$2" ".*${filetype}\$"` -ne 0 ] && FILETYPE=$i
done
let i+=1
done
if [ -z $FILETYPE ]; then
echo "HELP!!!"
exit;
fi
if [ "$1" == "extract" ]; then
echo ${EXTRACT[$FILETYPE]} "$2"
elif [ "$1" == "list" ]; then
echo ${LIST[$FILETYPE]} "$2"
else
echo 'bad argument'
fiДарю.
При небольших изменениях должен и не под bash работать... правда, не помню, что именно реализует array.
-
LXj
- Сообщения: 94
Re: Создание переменной по имени
Я просто ничего серьёзного в шелле не писал, а тут появилась идейка, которую решил с помощью шелла-таки и реализовать. Вот, скажем, у меня такое ощущение, что массивы в sh я вижу в первый раз.
Во-вторых, главная цель, которую я преследовал, когда придумывал формат "конфигурационной части" -- это расширяемость. Мой черновой вариант на самом деле во многом похож на предложенный вами код
Код: Выделить всё
## Detecting file format...
if [ `echo $2 | grep ".tar.gz$"` ]
then
export EXTRACT="tar -xzf"
export LIST="tar -lzf"
elif [ `echo $2 | grep ".tar.bz2$"` ]
then
export EXTRACT="tar -xjf"
export LIST="tar -ljf"
elif [ ` echo $2 | grep ".zip$" ` ]
then
export EXTRACT="unzip"
export LIST="unzip -l"
else
echo "Invoke bah as"
echo " bah action file"
echo "where action could be list or ex[tract]"
echo "and file must be .tar.gz, .tar.bz2, .zip"
fi
## Running archiver
if [ `echo $1 | grep "list"` ]
then
$LIST $2
elif [ `echo $1 | grep "ex"` ]
then
$EXTRACT $2
fi(это было написано до того, как я вообще читал какие-либо доки по шеллу, по-этому всё так плохо
Так вот, представьте, что ваш или мой старый вариант скрипта потребуется расширить (я ведь не для того его пишу, чтобы предоставить интерфейс к двум функциям трех программ, вы ведь понимаете?). Скажем, добавить поддержку ещё десятка архиваторов. Или ещё десятка различных действий. Придётся перекапывать весь код! В моём втором варианте нужное место локализуется очень просто. Добавление нового архиватора требует изменения всего в одном месте. Добавление нового действия немного сложнее, но по крайней мере не требует дополнительного if-а в другом конце скрипта. В идеале в один файл выносится $FORMATS, в другой -- определение сложных действий, а скрипт сокращается до десятка-двух строчек (пока опциями не обрастет, конечно
Да, может быть планы для шелла слишком наполеоновские и надо было писать на питоне/перле. Но зато этот скрипт заставил-таки меня нормально прочитать (и кое-что запомнить) доки к шеллу.
В общем, вопрос остается в силе
Update С точки зрения читабельности гораздо удобнее иметь всю информацию об одном формате архивов в одном месте, группировка же по действиям более удобна для реализации но для пользователя значения абсолютно не имеет
-
KiWi
- Бывший модератор
- Сообщения: 2521
- Статус: статус, статус, статус
Re: Создание переменной по имени
Код: Выделить всё
A="extract='tar -blah'"
eval "$A"
echo $extractПрекрасно работает.
Так что вам не нравится в моём коде?
Вы не поняли как добавить формат?
Элементарно. Добавляем строку в FORMATS и массивы действий.
С добавлением действий, как и в вашем, надо модифицировать конец скрипта.
Если уж так хочется всё динамическое:
Код: Выделить всё
#!/bin/bash
FORMATS=(
"\.tar\.gz \.tar\.Z \.tgz"
"\.tar\.bz2 \.tbz2"
)
ACT_EXTRACT=(
"tar -xzf"
"tar -xjf"
)
ACT_LIST=(
"tar -lzf"
"tar -ljf"
)
ACTS=(
"LIST"
"EXTRACT"
)
if [[ -z $1 || -z $2 ]]; then
echo 'help'
exit;
fi
FILETYPE=""
i=0
while [[ $i -lt ${#FORMATS[@]} && -z $FILETYPE ]]; do
for filetype in ${FORMATS[$i]}; do
[ `expr match "$2" ".\+${filetype}\$"` -ne 0 ] && FILETYPE=$i
done
let i+=1
done
if [[ -z $FILETYPE || " ${ACTS} " != *" $1 "* ]]; then
echo "bad filetype or bad action"
exit;
fi
TMP="\${ACT_$1[$FILETYPE]}"
ACTION=`eval echo $TMP`
echo $ACTION "$2"Про форматы -- не изменилось
Про действие -- добавляем в массив ACTIONS действие, и пишем для него
Код: Выделить всё
ACT_<новое действие>=(
"<команда для первой группы архивов>"
"<команда для второй группы архивов>"
...
)-
KiWi
- Бывший модератор
- Сообщения: 2521
- Статус: статус, статус, статус
Re: Создание переменной по имени
На самом деле, если бы мне было нужно, я бы сделал:
Код: Выделить всё
#!/bin/bash
FORMATS=(
".\+\.tar\.gz\$ .\+\.tar\.Z$ .\+\.tgz\$"
".\+\.tar\.bz2\$ .\+\.tbz2\$"
)
ACT_EXTRACT=(
"tar -xzf \$2"
"tar -xjf \$2"
)
ACT_LIST=(
"tar -lzf \$2"
"tar -ljf \$2"
)
ACTS=(
"LIST"
"EXTRACT"
)
if [[ -z $1 || -z $2 ]]; then
echo 'help'
exit;
fi
FILETYPE=""
i=0
while [[ $i -lt ${#FORMATS[@]} && -z $FILETYPE ]]; do
for filetype in ${FORMATS[$i]}; do
[ `expr match "$2" "${filetype}"` -ne 0 ] && FILETYPE=$i
done
let i+=1
done
if [[ -z $FILETYPE || " ${ACTS} " != *" $1 "* ]]; then
echo "bad filetype or bad action"
exit;
fi
TMP="\${ACT_$1[$FILETYPE]}"
TMP=`eval echo $TMP`
eval $TMP-
LXj
- Сообщения: 94
Re: Создание переменной по имени
Большое спасибо!
Нет, я-то понял, как их добавлять. Проблема в том, что ТАК их добавлять неудобно (что я подробно расписал в своём предыдущем сообщении). Конечно, это моё имхо. Но не стану же я писать скрипт, которым лично мне не удобно пользоваться?
Нет, я-то понял, как их добавлять. Проблема в том, что ТАК их добавлять неудобно (что я подробно расписал в своём предыдущем сообщении). Конечно, это моё имхо. Но не стану же я писать скрипт, которым лично мне не удобно пользоваться?
-
KiWi
- Бывший модератор
- Сообщения: 2521
- Статус: статус, статус, статус
Re: Создание переменной по имени
Давай построчно.
Так вот, представьте, что ваш или мой старый вариант скрипта потребуется расширить (я ведь не для того его пишу, чтобы предоставить интерфейс к двум функциям трех программ, вы ведь понимаете?). Скажем, добавить поддержку ещё десятка архиваторов. Или ещё десятка различных действий. Придётся перекапывать весь код!
"Весь" код -- это начало скрипта
В моём втором варианте нужное место локализуется очень просто.
А в моём как будто сложно?
Добавление нового архиватора требует изменения всего в одном месте.
Ну, а у меня 1 + количество действий, притом, что всё находится рядом.
Добавление нового действия немного сложнее, но по крайней мере не требует дополнительного if-а в другом конце скрипта.
Мой второй вариант имеет динамическое определение действий, если ещё дописать совсем немного, то можно сделать поддержку алиасов...
В идеале в один файл выносится $FORMATS, в другой -- определение сложных действий, а скрипт сокращается до десятка-двух строчек (пока опциями не обрастет, конечно smile.gif Но зато обработка опций не будет прилеплена намертво к конфигурации подключаемых архиваторов)
На самом деле, в bash'е не особо принято разделять на файлы...
Это всё-таки больше самостоятельные скрипты...
Лирическое отступление: вообще, у вас в 1 переменной получается одна большая помойка...
Вообще, по хорошему, надо решать задачу многомерными массивами... которых у нас нет, либо использовать несколько одномерных...
-
LXj
- Сообщения: 94
Re: Создание переменной по имени
Тут всё дело в личных эстетических пристрастиях. Вам не нравится большая помойка в одной переменной, мне не нравится, что информация о каждом архиве хранится в трех разных массивах.
Мне кажется, что удобней всё-таки, если информация сгруппирована по форматам. У вас же она сгруппирована в первую очередь по действиям.
Да, так вот, я уже выше написал, что в моем скрипте при выходе из цикла значения всех переменных сбрасываются. Нет ли случайно каких-нибудь догадок, почему это происходит?
UPD: Кажется, я понял. Цикл получает данные из фильтра, а потому выполняется в отдельном шелле. То есть так, как если бы я написал При выходе из этого шелла все переменные, конечно, и сбрасываются. Ну а поскольку не будет дружить с пробелами, мне точно нужно смотреть в сторону массивов
Мне кажется, что удобней всё-таки, если информация сгруппирована по форматам. У вас же она сгруппирована в первую очередь по действиям.
Да, так вот, я уже выше написал, что в моем скрипте при выходе из цикла значения всех переменных сбрасываются. Нет ли случайно каких-нибудь догадок, почему это происходит?
UPD: Кажется, я понял. Цикл получает данные из фильтра, а потому выполняется в отдельном шелле. То есть так, как если бы я написал
Код: Выделить всё
echo "$FORMATS" | (while read LINE; ... done)Код: Выделить всё
for LINE in $FORMATS-
madskull
- Сообщения: 1019
- Статус: Экс-металлюга
Re: Создание переменной по имени
(LXj @ Jun 15 2006, в 01:34) писал(а):UPD: Кажется, я понял. Цикл получает данные из фильтра, а потому выполняется в отдельном шелле. То есть так, как если бы я написал
Код
echo "$FORMATS" | (while read LINE; ... done)
При выходе из этого шелла все переменные, конечно, и сбрасываются. Ну а поскольку
Код
for LINE in $FORMATS
не будет дружить с пробелами, мне точно нужно смотреть в сторону массивов
LXj
Решений для твоей проблемы с пробелами и отдельным шеллом как минимум два:
Код: Выделить всё
while read LINE; ... done < <(echo "$FORMATS")и
Код: Выделить всё
IFS=$'\n'; for LINE in $FORMATS;do .... donePS. Прошу прощения, особо в весь топик не вникал и ответил только на отквоченное.
ArchLinux / IceWM
-
LXj
- Сообщения: 94
Re: Создание переменной по имени
Спасибо! Я решил всё-таки сделать через массив... Массив получился похож на двумерный
Код: Выделить всё
ACTS=(
"list"
"extract"
)
FORMATS=(
"PATTERNS='.\+\.tar\.gz\$ .\+\.tar\.Z$ .\+\.tgz\$';
extract='tar -xzf $2';
list='tar -tzf $2';
"
"PATTERNS='.\+\.tar\.bz2$ .\+\.tbz2$';
extract='tar -xjf $2';
list='tar -tjf $2';
"
"PATTERNS='.\+\.zip$';
extract='unzip $2';
list='unzip -l $2';
"
)
ALL_PATTERNS=""
FOUND=""
FILETYPE=""
i=0
while [[ $i -lt ${#FORMATS[@]} && -z $FILETYPE ]]; do
eval "${FORMATS[$i]}"
for filetype in $PATTERNS; do
[ `expr match "$2" "${filetype}"` -ne 0 ] && FILETYPE=$i
done
ALL_PATTERNS="$ALL_PATTERNS $PATTERNS"
let i+=1
done
if [[ -z $FILETYPE || " ${ACTS} " != *" $1 "* ]]; then
echo "BAH!"
echo "Usage:"
echo " bah <action> <filename> [<path>]"
echo "Where <action> is one of"
echo "list - list files in archive"
echo "extract - extract all files to current directory"
echo "BAH supports following archive types:"
echo $ALL_PATTERNS
exit;
fi
TMP=\$$1
eval $TMPПравда, проверка " ${ACTS} " != *" $1 "* некорректна. В общем, я сейчас хочу спать, допишу в другой раз
UPD: Да, предвидя вопросы об использованни $2 в FORMATS...
Одно из действий будет выглядеть так:
* Для tar-ов
$find = $list
* для zip-ов
$find = "unzip -l $2 | head -n-3 | tac | head -n-3 | tac | colrm 1 28 "
Отличие от find от list -- унифицированный формат для вывода. Ещё будет ls -- он будет выводить файлы только одной директории, по-этому в хелпе упоминается <path>
-
KiWi
- Бывший модератор
- Сообщения: 2521
- Статус: статус, статус, статус
Re: Создание переменной по имени
В чём её некорректность?
Берётся и смотрится если элемент в массиве...
Конечно же, учитывается регистр...
Получилось некое перепевание скрипта, притом, что в некоторых местах можно было бы и упростить/сделать просто по-другому...
Хотя нет, всё таки появилась нечто никому ненужное:
Код: Выделить всё
FOUND=""-
LXj
- Сообщения: 94
Re: Создание переменной по имени
У меня она почему-то не находит элементы массива, кроме list
Это ещё не конец
Не вычистилось из предыдущей версииIFL писал(а): ↑15.06.2006 10:20Хотя нет, всё таки появилась нечто никому ненужное:
Код: Выделить всё
FOUND=""
-
KiWi
- Бывший модератор
- Сообщения: 2521
- Статус: статус, статус, статус