Перебирать ключи карты в одной строке в bash

В Python я могу сделать это в одной строке:

>>> for key, value in dict(j=1, k=2).items():
...     print(key, value)
...
j 1
k 2
>>>

В bash я могу сделать это:

$ map=([j]=1 [k]=2); for key in ${!map[@]}; do value=${map[$key]}; echo $key $value; done
j 1
k 2
$

Есть ли в bash способ «встроить» объявление map в for - аналогично тому, как это возможно в примере с Python - вместо того, чтобы объявлять его заранее?

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

Чтобы внести ясность, общий ответ — «нет», если вы просто спрашиваете о доступном синтаксисе; но если у вас есть практическая проблема, которую этот синтаксис позволит вам решить, описание этой проблемы может позволить найти ответ с помощью альтернативного подхода.

Charles Duffy 01.07.2024 20:12

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

Charles Duffy 01.07.2024 20:13

Это может помочь объяснить вашу конечную цель и откуда берутся ваши данные (файл? аргументы командной строки? Как выглядит формат?). Для этой функции Python в bash нет прямого перевода, но, например, если ваша цель — просто перебрать пары значений, для этого есть инструменты в зависимости от того, как хранятся ваши данные.

tjm3772 01.07.2024 20:14

@CharlesDuffy У меня не обязательно есть проблемы с вышеизложенным, я просто ищу более короткую версию, то есть избегаю объявления двух дополнительных переменных (map и value)

levant pied 01.07.2024 20:15

@ tjm3772 Ответил в предыдущем комментарии, но позвольте мне также добавить к вопросу.

levant pied 01.07.2024 20:16

В вопросе вам не хватает объявления ассоциативного массива declare -A map. Как написано сейчас, map=([j]=1 [k]=2) создаст числовой индексированный массив с одной записью, значением 2 с индексом 0.

glenn jackman 01.07.2024 20:22

В целом, я бы сказал, что приоритезация краткости в bash сама по себе является ошибкой. Наследие десятилетий обратной совместимости, требующее, чтобы расширения и исправления находились в ранее неопределенном пространстве, означает, что нужно быть очень осторожным и явным; краткий код часто является кодом с тонкими (или не такими уж тонкими) ошибками (как показывает Гленн выше).

Charles Duffy 01.07.2024 20:25

@glennjackman Ах, должно быть, это остатки моего предыдущего тестирования, которое я проводил declare -A map, и это было во время сеанса, когда я опубликовал вопрос. Ну что ж...

levant pied 01.07.2024 20:36

Возможно, вам не хватает лучшего способа сделать то, что вам нужно, назовите это «Y», подходя к этому как «если идиома X — это то, как я подхожу к требованию Y в Python, то как мне реализовать X в bash», а не затем «как мне подойти к требованию Y в bash».

Ed Morton 02.07.2024 11:19

В Python dict(j=1, k=2) — это выражение, результатом которого является конкретный объект. В bash([j]=1, [k]=2) — это не выражение, а расширение синтаксиса присваивания, которое позволяет вам определить значение map без необходимости писать map[j]=1,map[k]=2. bash вообще не имеет значений массива, только специальный синтаксис, позволяющий сделать более или менее различные параметры похожими на один массив.

chepner 02.07.2024 22:44

@EdMorton По сути, мое последнее предложение. У меня есть команда, которой нужна пара ключ-значение. У меня есть список пар ключ-значение. Мне нужно запустить эту команду для каждой пары ключ-значение из этого списка. Путь Python для меня естественен. Я хочу повторить в bash. Оно должно быть коротким/легким для ввода/легким для запоминания. Оно должно читаться естественно слева направо. Он должен быть встроенным. for kv in kvs лучше, чем kvs=...; for k in kvs; v=kvs[k] или while read k v; ...; done <<<kvs. Не все возможно в bash, и некоторые из них субъективны, но чем ближе, тем лучше.

levant pied 03.07.2024 17:39

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

Ed Morton 03.07.2024 18:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
84
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Есть ли в bash способ «встроить» объявление карты в for — аналогично тому, как это возможно в примере с Python — вместо того, чтобы объявлять это заранее?

Нет.

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

printf "%s %s\n" j 1 k 2 | while read -r key value; do echo "$key" "$value"; done

Проверьте свои скрипты с помощью Shellcheck.

Чтобы избежать запуска цикла while в подоболочке, перенаправьте в него вводимые данные: while read ...; done <<< $'j 1\nk 2'

glenn jackman 01.07.2024 20:35
Ответ принят как подходящий

Нет никакого способа обойти это

declare -A map=([j]=1 [k]=2)

Расширение параметра "${!map[@]}" требует объявленного параметра.

На самом деле это неправда. Расширение параметра удачно расширяет неустановленную переменную в пустую строку.


Вы можете удалить переменную value

for key in "${!map[@]}"; do
  printf '%s %s\n' "$key" "${map[$key]}"
done

Обратите внимание, что ассоциативные массивы неупорядочены. Мой результат для этого цикла:

k 2
j 1

Если вы просто ищете быстрый способ проверить содержимое переменной, используйте declare -p

declare -A map=([j]=1 [k]=2)
declare -p map

результаты

declare -A map=([k] = "2" [j] = "1" )

Если у вас уже есть ассоциативный массив в Bash 5.2+, вы можете использовать параметр @k, который расширяется за счет чередования ключей и значений для разделения слов (см. 5.2.t в журнале изменений ), и printf потреблять два элемента одновременно :

declare -A map=([j]=1 [k]=2)
printf '%s %s\n' "${map[@]@k}"

Примечания:

  • хотя ключи и значения идут парами, последовательность пар неупорядочена (в отличие от индексированных массивов)
  • не путайте @k с @K, который был добавлен в версии 5.1 (см. 5.1.hh) и расширяется только до одного слова

Но если вы создали массив только для перебора пар ключ-значение, просто укажите и ключи, и значения непосредственно в printf:

printf '%s %s\n' j 1 k 2

Другие вопросы по теме