LINUX.ORG.RU
ФорумAdmin

вопрос по переменным в скрипте bash

 


1

3

Как быть если в процессе выполнения скрипта необходимо менять название переменной. Например переменная состоит из двух частей, одна часть название - слово, а вторая - коэффициент который должен меняться в процессе выполнения скрипта:

kof=1
per_$kof

Как такой переменной присвоить значение, на такое присвоение выдаёт ошибку:

per_$kof=22

★★

per_$kof=22

Что то я не пойму причём тут associative array.

При Bash 4:

declare -A per
kof=123
per[$kof]=22
declare -p per

blexey ★★★★★
()
Ответ на: комментарий от anymouze

я ж написал

Опять не то. ТС опирался на эту бредятину, пытаясь внушить, что его говноскрипт работает. Не надо ему потакать.

Deleted
()
Ответ на: а в чём проблема? от linuxnewbie

а в чём проблема?

Для чего этот огород? Это ж откровенная бредятина. Сначала eval "per_$kof=22", потом echo $per_1. Кого запутать пытаетесь?

Deleted
()
Ответ на: комментарий от anymouze

У меня должно было меняться название переменной и её значение. Огромное спасибо zvezdochiot!!! За то, что он обратил внимание на неправильную работу скрипта. Я это сразу и не заметил.
Дело не в том, что в цикле выводиться правильно, а за циклом выводиться не правильно, в цикле то же выводилось не правильно. Вот этот скрипт:

#!/bin/bash

kof=1

declare  per_$kof=0


for ((kof=1; kof<=10 ; kof++))
 do
   (( per_kof+=1 ))
   echo "per_$kof: $per_$kof"
done

Вот его результат:

per_1: 1
per_2: 2
per_3: 3
per_4: 4
per_5: 5
per_6: 6
per_7: 7
per_8: 8
per_9: 9
per_10: 10

Цисла от 1 до 10, это на самом деле не числа - не значение переменной, а название переменной этой переменной $per_$kof - например per_4. Просто $per_ не определена.

Если взять вот такой скрипт:

#!/bin/bash

kof=1

per_="per_"

declare  per_$kof=0


for ((kof=1; kof<=10 ; kof++))
 do
   (( per_kof+=1 ))
   echo "per_$kof: $per_$kof"
done

то результат будет вот таким:

per_1: per_1
per_2: per_2
per_3: per_3
per_4: per_4
per_5: per_5
per_6: per_6
per_7: per_7
per_8: per_8
per_9: per_9
per_10: per_10

Он получился таким потому, что переменная $per_ определена и равна такому же слову per_

Это конечно не то, что задумывалось мной.

Вот в такую ловушку можно попасть, а потом в другом скрипте долго думать почему не работает.
Ещё раз спасибо zvezdochiot что заметил ошибку.

Работающий скрипт вот:

#!/bin/bash


for ((kof=1; kof<=10; kof++))
 do
   eval per_$kof=$kof
   eval c='$'per_$kof
   echo "per_$kof: $c"
done


eval c='$'per_1
echo "$c"
eval c='$'per_2
echo "$c"
eval c='$'per_3
echo "$c"
eval c='$'per_4
echo "$c"
eval c='$'per_5
echo "$c"
eval c='$'per_6
echo "$c"
eval c='$'per_7
echo "$c"
eval c='$'per_8
echo "$c"
eval c='$'per_9
echo "$c"
eval c='$'per_10
echo "$c"
eval c='$'per_11
echo "$c"
eval c='$'per_47
echo "$c"

Вот результат его работы:

per_1: 1
per_2: 2
per_3: 3
per_4: 4
per_5: 5
per_6: 6
per_7: 7
per_8: 8
per_9: 9
per_10: 10
1
2
3
4
5
6
7
8
9
10


Это уже то что я задумывал.

Как видно вместо переменных per_11, per_47 выводится пустое место, так как они не определены.

Как в этом скрипте вообще избавиться от eval я не знаю.

eval per_$kof=$kof

можно заменить на

declare per_$kof=$kof

Но вот как в echo вставить значение этой переменной не прибегая к промежуточной переменной «c» и eval у меня не получилось.

v4567 ★★
() автор топика
Ответ на: комментарий от Deleted

Мне показалось, гражданин в стартовом посте хотел именно этого — создать кучу переменных с полученными значениями для каждого коэффициента, а далее уже обращаться как $переменная_коэффициент, для каждого полученного коэффициента. Т.е. именно такой подход и работает.

linuxnewbie
()
Ответ на: комментарий от Deleted

Ещё раз хочу поблагодарить zvezdochiot

Я не заметил ошибку. Думал бы что тот скрипт правильный.

v4567 ★★
() автор топика
Ответ на: комментарий от Deleted

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

linuxnewbie
()
Ответ на: комментарий от Deleted

Так eval «"_1=123» (per=") и не прокатит, переменная не может начинаться с _. К тому же, если переменная per по какой-то причине пустая строка, то это уже скорее логическая ошибка.

linuxnewbie
()
Ответ на: комментарий от Deleted

legolegs писал

И ведь наверняка конечная задача - какая-нибудь тривиальщина уровня «переименовать 1000 файлов», где на самом деле даже массивы не нужны.

Да работать надо с папками и файлами в них: сравнение, переименование и т.д. при этом заренее ничего не известно. Не известно сколько папок, глубина их вложенности, их названия, какие файлы в папках, есть ли файлы они вообще, и т.д.

И как тут обойтись без массивов?

Я не спорю, может я просто не знаю.

v4567 ★★
() автор топика
Ответ на: комментарий от linuxnewbie

Так eval ««_1=123» (per=») и не прокатит

Так то да. Только в исполнении ТС per_ не переменная, а часть текста. Он не заменяется, а $kof заменяется.

Deleted
()
Ответ на: комментарий от v4567

А с чего такая нелюбовь к массивам? У Вас же баш, не стесняйтесь! Да и в zsh возможно будет работать, а других шеллов как известно не существует. Иначе бы взяли вместо баша что-нибудь более универсальное.

linuxnewbie
()
Ответ на: комментарий от linuxnewbie

Подобное построение в sh неудобно от слова «совсем». Ты с ним замучаешься только. Удобным в данном случае будет содержать в одной переменной сразу несколько значений и использовать их по необходимости.

А если надо сравнить папки, то есть сравнить всё что есть в этих папках: файлы, посмотреть куда идут ссылки, если они есть и т.д. Известны только названия папок, глубина вложенности не известна, сколько папок и файлов не известно, а потом ещё в добавок надо сформировать отчёт. Думаю через переменные перезаписывая их сделать будет намного, очень сильно намного трудней чем если всё содержимое папки запихнуть в массив - массивы и уже работать с ними.

v4567 ★★
() автор топика
Ответ на: комментарий от Deleted

Дописывая, а как потом узнать место в этой переменной, где лежит нужная мне информация и вынут её. И если уж всё дописывать в переменную, то почему тогда не воспользоваться массивом, по сути та же переменная, только например я уже знаю как вынуть то что мне нужно.

v4567 ★★
() автор топика
Ответ на: комментарий от v4567

почему тогда не воспользоваться массивом

Так пользуйся. Тебе уже показали как. И многократно. Только это два разных подхода - список vs массив. У каждого свои плюсы и конечно же минусы.

Deleted
()
Ответ на: комментарий от v4567

В интернете говорят, что у баша нет ограничений на размет переменных, но эти ограничения могут наклазываться операционной системой. Также у баша нет ограничений на размер массивов, они могут быть настолько большими, насколько хватает памяти. Да и банально удобней и быстрее оперировать массивами, особенно если там замешаны пробелы-переносы в строках.

Касательно данной задачи, не почему-то хочется прогнать find несколько раз и сохранять получаемые в процессе результаты для отчёта. Особенно учитывая, что конструкция вида

find -print0 | command --delimiter=$'\0'

это, по-сути, единственный способ работы с произвольными файлами в bash.

linuxnewbie
()
Ответ на: комментарий от Deleted

Согласен: у него не работает. Я это проверял. И я показал как, это может работать. Но... мне самого начала эта идея представлялась не нужной. Поэтому, я утверждаю

  • да сделать возможно
  • необходимости в таком коде нет
anymouze ★★
()
Ответ на: комментарий от v4567

Я тебе что-то другое в коде писал? Вот то, что не работает. А это работает. Ах, да я не поправил твоё проверочное сообщение. Ладно, бороться за первенство в написании не буду — гордиться подобным кодом я бы не стал.

Как видно вместо переменных per_11, per_47 выводится пустое место, так как они не определены.

Ну, как это не определены. Это ж bash scripting. Эти переменные «возникают» в момент «обращения» к ним т.е. в команде echo .... В цикле инициализации остальных переменных они не участвовали, поэтому echo ... ничего и не печатает. Условно говоря, они получили значение "". Вот попробуй добавить в скрипт такие строки и поясни мне вывод

echo $per_44
echo $per_45
echo $per_10+$per_45
echo $((per_10+$((per_45))))

Как в этом скрипте вообще избавиться от eval я не знаю.

Не получится. Я уже «жевал это».

=====
linuxnewbie

переменная не может начинаться с _.

Я разрешаю :) — может:

#!/bin/bash
###
_v='переменная'
echo $_v
_1=1
echo $_v' '_1' имеет значение '$_1' ''a '$_v' '_2' нет'$_2
###

anymouze ★★
()
Ответ на: комментарий от anymouze

Да, спасибо. Всё дело было в том, что переменная не может содержать спецсимволов, и андерскор не является спецсимволом.

linuxnewbie
()
Ответ на: комментарий от linuxnewbie

переменная не может содержать спецсимволов

;) это же хорошо. на то, они и спецсимволы.

 $ cc='df'
 $ eval "as'${cc}'_55=25"                                                                                                                                    
bash: asdf_55=25: команда не найдена
а теперь тоже самое, но без кавычек — опять «команда не найдена»?

anymouze ★★
()
Ответ на: комментарий от anymouze

Вот так?

 $ cc='df'
 $ eval as'${cc}'_55=25
bash: asdf_55=25: команда не найдена
 $ cc='df'
 $ as${cc}_55=25
bash: asdf_55=25: команда не найдена
 $ eval as${cc}_55=25
 $ echo $asdf_55
25
linuxnewbie
()
Ответ на: комментарий от legolegs

Какой же хренью занимаются в этом треде. Родина дала тебе массивы, структурируй данные - не хочу, хочу жрать eval!

Да вы готовы романы писать, чтобы обосновать свою идиосинкразию к eval! Хотя ведь очевидно, что ТС сдал бы свой зачёт только если применил простые массивы, (а не асcоциативные, которые тут втирают).

vodz ★★★★★
()
Ответ на: комментарий от v4567

Если кто знает, дайте ссылки на документацию (только на русском) по bash, что бы было всё, подробно и разжёвано для домохозяек.

Самое лучшее что мне удалось найти на просторах интернета вот: http://rus-linux.net/MyLDP/BOOKS/abs-guide/flat/abs-book.html

Но там углублённый материал как то плохо раскрыт.

v4567 ★★
() автор топика
Ответ на: комментарий от v4567

Так на русском, кроме ABF, и нет ничего толком нормального. А чего ущё углублённей хочется, там вроде всё есть. ну, разве что, магии типа Разбор или создание массива в bash (комментарий) может не быть (давно читал, не помню, разбирается ли там подобное).

gremlin_the_red ★★★★★
()

Тред не читал, eval советовали?

$ suffix=1
$ eval prefix_${suffix}=OLOLO
$ echo ${prefix_1}
OLOLO

IPR ★★★★★
()
Ответ на: комментарий от linuxnewbie

Очевидно же — без кавычек вот эта строка $ eval "as'${cc}'_55=25" должна быть

$ cc='df'
$ as${cc}_55=25
bash: asdf_55=25: команда не найдена
$ eval as${cc}_55=25     <== через eval и без кавычек
$ echo $asdf_55
25

anymouze ★★
()
Ответ на: комментарий от legolegs

Конечный результат запостм потом, мы поугараем.

Поиздеваться все могут, много ума не надо. legolegs если такой умный, тогда расскажи как бы ты сделал, если бы тебе надо построить дерево файлов и каталогов, при этом заранее не известна глубина вложенности каталогов и их количество в каталоге самого верхнего уровня ив других уровнях то же.

v4567 ★★
() автор топика
Ответ на: комментарий от v4567

Я бы воспользовался командой tree или ls -R если надо просто вывести на экран. Или find. Кроме того, придя на ЛОР задавать вопросы я бы сначала обрисовал желаемый конечный результат, напр

построить дерево файлов и каталогов

а не в конце двухстраничного треда.

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 1)
Ответ на: комментарий от anonymous

За ncdu спасибо, но по моему для меня это немного не то.

а не в конце двухстраничного треда.

я не спрашиваю как построить дерево каталогов, я это сделаю и сам. И если я буду использовать eval то при работе скрипта с ним проблем не будет. Я просто спрашивал некоторые моменты, всего знать невозможно.

Вот ещё хочу спросить, как отловить символ перевода строки для его замены на точку или на слово «_enter_». Если вводить «\n» как ескейп последовательность, то это будет интерпретироваться как перевод строки, но если перевод строки стоит в файле, то при работе с таким файлом этот перевод строки уже будет интерпретироваться не как «\n». Или я не прав? Во всяком случае такая конструкция не работает:

aqw="/dir" ; for per in "$aqw"* "$aqw".* ; do echo "$per" | sed -e 's/\n/./g' ; done

v4567 ★★
() автор топика
Ответ на: комментарий от v4567

Вот ещё хочу спросить, как отловить символ перевода строки для его замены на точку или на слово «enter».

Это отдельная тема и решения бывают разные. sed ты знаешь, ещё есть tr '\n' '.' и printf "%q\n" $'new\nline'

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 1)
Ответ на: комментарий от legolegs

с sed-ом не работает, с tr работает. Только теперь убрались и переносы в конце строк. Сыграл вот такой вариант:

aqw="/dir/" ; for per in "$aqw"* "$aqw".* ; do echo "$per" | tr '\n' '.' ; echo ; done

v4567 ★★
() автор топика
Ответ на: комментарий от legolegs
printf "%s" "$per"

Не работает, ведь не будет перевода строки после каждого названия файла.

Лучше не использовать echo. Что будет, если файл называется -n?

У меня ведь «\n» замениться на точку, как замениться на точку и перевод строки после каждого файла, а echo как раз и поставит перевод строки в конце каждого файла, в прежнем варианте в каждом названии файла в конце появлялась точка.

aqw="/dir/" ; for per in "$aqw"* "$aqw".* ; do echo "$per" | tr '\n' '.' ; echo ; done

Вот окончательный вариант без точки:

aqw="/dir" ; for per in "$aqw"* "$aqw".* ; do str=$(echo "$per" | tr '\n' '.' ; echo) ; printf "%s\n" "$str" | sed -e 's/.$//' ; done

v4567 ★★
() автор топика
Ответ на: комментарий от v4567

aqw=«/dir» ; for per in «$aqw»* «$aqw».*

Это раскроется в for per in /dir* /dir.* т.е. все файлы, с именами начинающимися с букв dir

legolegs ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.