LINUX.ORG.RU

Bash Hackers Wiki

 , , ,


2

2

Наткнулся в сети на вику, которая открыла для меня новые глубины боли Bash. Да так, что «трюки» с арчвики показались мне цветочками...

Короче, сабж: http://wiki.bash-hackers.org

Для оценки ситуации с «белым пушистым зверьком» (aka «песец») в Bash, предлагаю взглянуть хотя б на две странички:

Может кому пригодится...

Для Ъ

callFuncs() {
    # Set up indirect references as positional parameters to minimize local name collisions.
    set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'}

    # The only way to test for set but null parameters is unfortunately to test each individually.
    local x
    for x; do
        [[ $x ]] || return 0
    done

    local -A a=(
        [foo]='([r]=f [s]=g [t]=h)'
        [bar]='([u]=i [v]=j [w]=k)'
        [baz]='([x]=l [y]=m [z]=n)'
        ) ${4+${a["$1"]+"${1}=${!3}"}} # For example, if "$1" is "bar" then define a new array: bar=([u]=i [v]=j [w]=k)

    ${4+${a["$1"]+"${!4-:}"}} # Now just lookup the new array. for inputs: "bar" "v", the function named "j" will be called, which prints "j" to stdout.
}

main() {
    # Define functions named {f..n} which just print their own names.
    local fun='() { echo "$FUNCNAME"; }' x

    for x in {f..n}; do
        eval "${x}${fun}"
    done

    callFuncs "$@"
}

main "$@"

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

Такое чувство, что в микроволновке режим «разморозка» и «максимальная мощность» перепутали...

KennyMinigun ★★★★★
() автор топика

Анонимус давно говорил, это поделие лучше не трогать даже длинной палкой если есть возможность.

anonymous
()

предлагаю взглянуть хотя б на две странички

Фи, что за тривиальщину ты сюда принёс? Это же всё в мане прописанно и разжованно.

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

Манами пользуются как справочником во время упражнений/работы, а не «читают» как макулатуру.

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

Это же всё в мане прописанно и разжованно.

Меня туда привел поиск «как передать associative array аргументом функции» после того, как я наткнулся на решение через eval: https://stackoverflow.com/questions/4069188/how-to-pass-an-associative-array-...

На локалхосте (Ubuntu, Bash 4.4) вышло довольно неплохо:

# как пример использрования, не надо меня тыкать в declare -p
function dump() {
  local -n map=$1

  local key=
  for key in "${map[@]}"; do
   echo $key ' -> ' ${map[$key]}
  done
}

declare -A numbers=([one]=1 [two]=2 [three]=3)
dump numbers

Но потом оказалось, что на продукционной тачке (RHEL, Bash 4.1) у declare нет опции -n. Пришлось искать другой способ. Как раз на вике про массивы я нашел вдохновение*:

local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'

* Для желающих challenge: переписать dump, чтоб работало, не используя declare -n (local -n тоже)

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

Манами пользуются как справочником во время упражнений/работы, а не «читают» как макулатуру.

$ man bash | wc -l
3553

$ man bash | wc -w
46050

Ну-ну.

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

Это же всё в мане прописанно и разжованно.

Кстати, формат подачи отличается.

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

Не знаешь на какой кнопке в мане поиск?

Зависит от $PAGER. У меня less и /. А теперь скажи мне как найти справку (обязательно в мане) по, например, builtin declare.

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

local -a 'xkeys=(«${!'»$1"'[@]}")' 'ykeys=(«${!'»$2"'[@]}")'
* Для желающих challenge: переписать dump, чтоб работало, не используя declare -n (local -n тоже)

Да наздоровье:

local -a "xkeys=(\"\${!$1[@]}\")"
Ахренеть как сложно...

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 2)
Ответ на: комментарий от KennyMinigun
man bash
-N
Enter
&BUILTIN
Enter (здесь запоминаем номер строки с заголовком SHELL BUILTIN COMMANDS - у меня это 2041)
&
Enter
2041g
Enter
/declare
Enter

Не быстро, но всяко быстрее, чем гуглить

shell-script ★★★★★
()
Ответ на: комментарий от stave

И тут сразу вспоминается перл.

Ну тут делается что надо: в $1 - имя нашей переменной, мы хотим («${!var[@]}») — это уже сам синтаксис, а не специзвращение, но для отложенной обработки экранируем спец символы. Можно и так:

local -a xkeys\=\(\"\${!$1[@]}\"\)
#либо
local -a 'ykeys=("${!'$1'[@]}")'

UPD: а парсеру башню то сносит... :)

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

Никогда так ни делай. Возьми перл, пистон. Все кто такое выдаёт попадают в программисткий ад и пишут на басике вечность. Так что не искушай судьбу.

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

Что только не придумают, лишь бы пайпами пользоваться.

Fix*

* Bash == программирование на пайпах.

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

Никогда так ни делай. Возьми перл, пистон. Все кто такое выдаёт попадают в программисткий ад и пишут на басике вечность. Так что не искушай судьбу.

Я и не делаю. Просто колеги с соседней команды постоянно кидают code review на свое поделие в Bash. А я каждый раз упоминаю, чтоб переписали на Perl/Python. А они каждый раз отвечают «мало времени и изменение небольшое, лучше посмотри что есть». Вот и приходится ревьювить...

Даже мысль изначальная с ассоциативными массивами не моя. Коллеге захотелось чтоб «как в питоне» (sic!), только нифига не получалось.

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

Коллеге захотелось чтоб «как в питоне»

Как в питоне в баше плохо. В баше хорошо как в баше:

while IFS=, read -r key value; do echo "$key  ->  $value"; done <<EOF
one,1
two,2
tree,3
EOF
legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 1)
Ответ на: комментарий от legolegs

О, кстати, напомнило один лулз:

IDS=()
find /some/dir -type f -name '*.data' | while read file; do
  local keyword=$(grep -o '^id: [0-9a-z]\*' | sed 's/id: //')
  IDS+=("$keyword")
done

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

Все новое — хорошо забытое старое. Да и мало кто полезет в истории копаться.

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

Вам просто не нравятся ассоциативные массивы? Ну то что массивы в shell привнесли сбоку без ключевых слов как, скажем, в awk для обхода по key, скорее вышло стилизовано к встроенному «массиву» $*/$@. Получился синтаксис откровенно не очень. Но это не отменяет тот факт, что если они есть, то многие задачи решаются с помощью их весьма элегантно. Вот закрыть глаза на синтаксис, и жить можно. Скажем: Поиск и удаление элемента массива (комментарий)

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

О, кстати, напомнило один лулз:

Отвратительно. Прямо скажем, IDS мало напоминает IFS, читать find через пайп без установки IFS и без -r — пропадут начальные и конечные пробелы. А уж grep | sed в качестве красивой демонстрации...

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

Вам просто не нравятся ассоциативные массивы?

В баше - не нравятся. По факту получается, что для передачи в функцию надо либо объявить массив глобальным и передавать его имя, либо сериализовать массив. Про глобальные переменные всё уже давно сказано, про лишний код для сериалтзации/десериализации, тащемта, тоже. И да, синтаксис страшный. Некоторые примеры из треда напоминают указатели на методы классов в крестах, только ещё страшнее.

Я предпочитаю писать простой и прямой код, покуда это возможно.

PS Кстати, код Bash Hackers Wiki (комментарий) у меня не заработал, не печатает ничего справа от "->".

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

Упс, набросал на быструю руку. Имелось ввиду

IDS=()
find /some/dir -type f -name '*.data' | while read file; do
  local keyword=$(grep '^id: [0-9a-z]\+' "$file" | sed 's/id: //')
  IDS+=("$keyword")
done
echo "${IDS[@]}" 

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

IDS перед | не тот, что до.

Ага, сабшелл. Потому и лулз

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

А делали бы «find -exec grep +» и всё было бы хорошо.

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

По факту получается, что для передачи в функцию

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

PS Кстати, код Bash Hackers Wiki (комментарий) у меня не заработал, не печатает ничего справа от "->".

У вас есть ключ -n у local?

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

Можно подумать, не массивы при передачи в функцию ведут как-то иначе

Ну уж глобально их объявлять не приходится. Да и вообще объявлять, например func $(whoami) и всё. А с массивами декляре, локал, ахалай-махалай.

У вас есть ключ -n у local?

Да, есть. Обычная федора 28.

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

Веб-сервер на баше уже написал

А собирался? 😳

шиз

Перед подписью лучше точку ставить, а не запятую.

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

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

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

Веб-сервер на баше уже написал

А собирался?

Не знаю что ты там собирался
любителей писать веб-сервера на баше

А ты реально поехавший.

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

Меня man declare выбросил на bash_builtins(1)

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