более элегантный способ сделать массив, используя косвенную ссылку в bash [duplicate]

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

44
задан Ciro Santilli 新疆改造中心 六四事件 法轮功 10 June 2015 в 05:14
поделиться

13 ответов

Если в гнезде вы имеете в виду что-то вроде этого:

#!/bin/bash

export HELLO="HELLO"
export HELLOWORLD="Hello, world!"

echo ${${HELLO}WORLD}

Тогда нет, вы не можете вставлять выражения ${var}. Расширитель синтаксиса bash не поймет этого.

Однако, если я правильно понимаю вашу проблему, вы можете посмотреть на команду basename - она ​​удаляет путь из заданного имени файла, и если задано расширение, также разделит это. Например, запуск basename /some/path/to/script.sh .sh вернет script.

38
ответ дан Tim 19 August 2018 в 18:51
поделиться
  • 1
    +1 для ссылки на базовое имя. – Andrew Coleson 27 May 2009 в 20:09
  • 2
    Если вы используете индексы, они могут быть, правильно? например, ARR=('foo' 'bar' 'bogus'); i=0; while /bin/true ; do echo ${ARR[$i]} ; i=(( (i + 1) % 3 )); done, который, очевидно, бесполезен как код, но работает в качестве примера. – Ian Stapleton Cordasco 7 January 2013 в 23:58
  • 3
    На самом деле, этот поддерживается в некоторых случаях, по крайней мере, в bash и ksh. Это работает: x=yyy; y=xxxyyy; echo ${y%${x}}. Я думаю, что важный бит состоит в том, что вложенное расширение является аргументом одного из операторов. Однако я не видел, что это хорошо документировано. – twalberg 8 January 2013 в 00:07
  • 4
    Это не работает для меня, я получаю bash: $ {$ {HELLO} WORLD}: неправильная замена. Есть идеи? Я нахожусь на ферме. – Carlos Bribiescas 20 January 2016 в 21:27
  • 5
    @CarlosBribiescas, как сказал @Tim, «нет, вы не можете вставлять выражения». В примере echo ${${HELLO}WORLD} показана невозможность this . – Atcold 27 February 2017 в 21:25

basename bultin может помочь с этим, поскольку вы специально раскалываете / в одной части:

user@host# var=/path/to/file.extension
user@host# basename ${var%%.*}
file
user@host#

Это не намного быстрее, чем вариант с двумя линиями, но это всего лишь одна строка, использующая встроенная функциональность. Или, используйте zsh / ksh, который может делать объекты, вложенные в шаблон. :)

1
ответ дан dannysauer 19 August 2018 в 18:51
поделиться
  • 1
    извините за форматирование. Просто попробуйте. :) var = $ (basename $ {var %%. *}) – dannysauer 27 May 2009 в 20:45
  • 2
    или использовать базовое расширение базового имени bash, как сказал Тим, и я пропустил ...: D – dannysauer 27 May 2009 в 20:47

Старая нить, но, возможно, ответ - использование Indirection: $ {! PARAMETER}

Например, рассмотрим следующие строки:

H="abc"
PARAM="H"
echo ${!PARAM} #gives abc
10
ответ дан Deepak 19 August 2018 в 18:51
поделиться

Я знаю, что это древняя тема, но вот мои 2 цента.

Вот функция bash (по общему признанию kludgy), которая позволяет использовать требуемые функции:

read_var() {
  set | grep ^$1\\b | sed s/^$1=//
}

Вот короткий тестовый скрипт:

#!/bin/bash

read_var() {
  set | grep ^$1\\b | sed s/^$1=//
}

FOO=12
BAR=34

ABC_VAR=FOO
DEF_VAR=BAR

for a in ABC DEF; do
  echo $a = $(read_var $(read_var ${a}_VAR))
done

Выход, как и ожидалось:

ABC = 12
DEF = 34
1
ответ дан diegog 19 August 2018 в 18:51
поделиться

Выражения вроде ${${a}} не работают. Чтобы обойти это, вы можете использовать eval:

b=value
a=b
eval aval=\$$a
echo $aval

Выход:

value

2
ответ дан Gavin Smith 19 August 2018 в 18:51
поделиться

eval позволит вам делать то, что вы хотите:

export HELLO="HELLO"
export HELLOWORLD="Hello, world!"

eval echo "\${${HELLO}WORLD}"

Выход: Hello, world

-1
ответ дан jgshawkey 19 August 2018 в 18:51
поделиться

На самом деле можно создать вложенные переменные в bash, используя два шага.

Вот тестовый скрипт, основанный на записи Тима, используя идею, предложенную пользователем1956358.

#!/bin/bash
export HELLO="HELLO"
export HELLOWORLD="Hello, world!"

# This command does not work properly in bash
echo ${${HELLO}WORLD}

# However, a two-step process does work
export TEMP=${HELLO}WORLD
echo ${!TEMP}

Выход:

Hello, world!

Существует множество опрятных трюков, которые объясняются запуском «info bash» из командной строки, а затем поиск «Расширение параметра Shell». Сегодня я читал несколько, просто потерял около 20 минут своего времени, но мои скрипты будут намного лучше ...

Обновление: после более подробного изучения я предлагаю эту альтернативу за ваш начальный вопрос.

progname=${0##*/}

Он возвращает

bash
6
ответ дан jmw86069 19 August 2018 в 18:51
поделиться
  • 1
    ОП спросил, возможно ли вложенное расширение в bash, и в основном вы отвечаете (что правильно): Нет, вам нужно развернуть выражение one во временную переменную, а затем использовать второе выражение для этой переменной. Однако вложенность шаблонов поддерживается в других оболочках. – Christian Herenz 5 June 2018 в 12:36

Для меня работал следующий параметр:

    NAME="par1-par2-par3"
    echo $(TMP=${NAME%-*};echo ${TMP##*-})

Выход:

    par2
10
ответ дан luferbraho 19 August 2018 в 18:51
поделиться
  • 1
    это лучший ответ – Eben Geer 11 January 2017 в 22:26
  • 2
    Brilliant. Я только удивляюсь, почему без второго (внутреннего) echo он рассматривает par2 как команду? – A S 16 April 2018 в 00:17

Это вложение не представляется возможным в bash, но оно работает в zsh:

progname=${${0%.*}##*/}
6
ответ дан Nathan Kitchen 19 August 2018 в 18:51
поделиться

Если мотивация состоит в том, чтобы «обфускать» (я бы сказал, упростил) обработку массива в духе «понимания» Python, создайте вспомогательную функцию, которая выполняет последовательности операций.

function fixupnames()
   {
   pre=$1 ; suf=$2 ; shift ; shift ; args=($@)
   args=(${args[@]/#/${pre}-})
   args=(${args[@]/%/-${suf}})
   echo ${args[@]}
   }

Вы может использовать результат с хорошим однострочным.

$ echo $(fixupnames a b abc def ghi)
a-abc-b a-def-b a-ghi-b
0
ответ дан nobar 19 August 2018 в 18:51
поделиться

Bash поддерживает косвенное расширение:

$ FOO_BAR="foobar"
$ foo=FOO
$ foobar=${foo}_BAR
$ echo ${foobar}
FOO_BAR
$ echo ${!foobar}
foobar

Это должно поддерживать вложенность, которую вы ищете.

35
ответ дан Sparhawk 19 August 2018 в 18:51
поделиться

В исходном вопросе OP есть однострочное решение, базовое имя скрипта с расширением файла:

progname=$(tmp=${0%.*} ; echo ${tmp##*/})

Вот еще один, но, используя чит для basename:

progname=$(basename ${0%.*})

. Другие ответы ушли от исходного вопроса OP и сосредоточились на том, можно ли просто расширить результат выражений с помощью ${!var}, но столкнулись с ограничением, которое var должно явно соответствовать имени переменной. Сказав это, нет ничего, что помешало бы вам получить ответ с 1 линером, если бы вы связали выражения вместе с точкой с запятой.

ANIMAL=CAT
BABYCAT=KITTEN
tmp=BABY${ANIMAL} ; ANSWER=${!tmp} # ANSWER=KITTEN

Если вы хотите, чтобы это выглядело как один оператор, вы можете вложить его в подоболочке, т. е.

ANSWER=$( tmp=BABY${ANIMAL) ; echo ${!tmp} ) # ANSWER=KITTEN

Интересное использование - косвенное действие на аргументы функции bash. Затем вы можете встраивать вызовы функций bash для достижения многоуровневой вложенной косвенности, потому что нам разрешено делать вложенные команды:

Вот демонстрация косвенности выражения:

deref() { echo ${!1} ; }
ANIMAL=CAT
BABYCAT=KITTEN
deref BABY${ANIMAL} # Outputs: KITTEN

демонстрация многоуровневой косвенности через вложенные команды:

deref() { echo ${!1} ; }
export AA=BB
export BB=CC
export CC=Hiya
deref AA # Outputs: BB
deref $(deref AA) # Outputs: CC
deref $(deref $(deref AA)) # Outputs: Hiya
1
ответ дан Stephen Quan 19 August 2018 в 18:51
поделиться
  • 1
    Спасибо за объяснение косвенности! Это действительно немного неудачно, что его можно использовать только для ссылки на существующие имена переменных. Конечно, oneliner можно сделать, но с назначением временной переменной я пытаюсь избежать в конце дня. – Steven Lu 7 February 2018 в 00:14
  • 2
    @ steven-lu как насчет моей функции deref()? Если у вас это объявлено, вы можете иметь свой oneliner без временной переменной? – Stephen Quan 7 February 2018 в 00:48
  • 3
    С помощью любой разумной меры это примерно в два-три раза более запутанно для обслуживания, чем использование переменной temp – Steven Lu 7 February 2018 в 17:52

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

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

e=($(find . -maxdepth 1 -type d))
c=${2:-${e[$((RANDOM%${#e[@]}))]}}

Объяснение: e - массив имен каталогов, c выбранный каталог, либо явно обозначенный как $ 2,

${2:-...}  

, где ... - альтернативный случайный выбор, заданный

${e[$((RANDOM%${#e[@]}))]}  

, где число

$((RANDOM%...))  

, генерируемое bash, делится на количество элементов в массиве e, заданное

${#e[@]}  

, что дает остаток (из оператора%), который становится индексом массива e

${e[...]}

Таким образом, у вас есть четыре вложенных разложения.

0
ответ дан user985675 19 August 2018 в 18:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: