как сделать сдвинуть значение массива в bash

set '%s_q1.out %s_q2.out %s_q3.out\n'
:|for d in several directories
do      cd  -P -- "$d" || ! break
        set "$1" "../${PWD##*/}"
        {   sed -ne'\|\( \.\./a_a_q[123]\.out\)\{3\}|q;s/.//p'
            printf "$@" "$2" "$2";  cut -c2-
        } <<-IN >./infile
        $(paste -d\  - ./infile)
        IN
done

Это должно работать с POSIX sed и bash , zsh , кш , мкш оболочка и многие другие. С оболочкой тире или yash здесь-документ вряд ли будет подкреплен обычным файлом, поэтому вы не можете полагаться на функцию lseek () , необходимую для поддержка совместного использования ввода. В этих случаях вам следует переключиться на одну из вышеупомянутых оболочек.

Если вы используете GNU sed , вы захотите использовать sed -une вместо просто sed -ne .

Вот разбивка:

  1. Сначала мы устанавливаем аргумент, который мы будем снова и снова использовать в качестве строки формата printf , и устанавливаем оба параметра $ PWD и $ OLDPWD - .

  2. Затем мы начинаем перебирать элементы в нескольких каталогах . Независимо от того, являются ли это каталоги или символические ссылки, мы получаем физический канонический путь -P в $ PWD и устанавливаем его последний компонент пути в $ 2 .

  3. Следующее, что происходит, происходит внизу при перенаправлении heredoc. paste считывает все ./ infile в какой-то безопасный временный файл, который оболочка защитила для нас, добавляя при этом один пробел к каждой строке.Затем оболочка устанавливает для этого временного файла значение stdin, и ./ infile снова назначается - но на этот раз как stdout.

  4. sed очень мало редактирует. Пока он не встретит вашу строку соответствия, он удаляет первый символ из каждой строки и p стирает результаты. Как только он встречает вашу строку, он q полностью использует ввод - ничего не печатает.

  5. Но printf печатает ../$ {PWD ## * /} _ q

    1. .out , за которым следует перевод строки в стандартный вывод, а затем вырезать ] берет стандартный ввод там, где sed оставил его (сразу после строки соответствия) , и считывает остальное, удаляя первый байт из каждой строки ввода.

    Таким образом, весь отредактированный ./ infile сводится к тому месту, откуда он появился, в одном куске.

    О, я это проверил.

    Я заменил бит нескольких каталогов на путь / tmp и сделал ...

    {   seq 7
        echo '../a_a_q1.out ../a_a_q2.out ../a_a_q3.out'
        seq 9 14
        printf %s\\n '' '' ''
    }  >/tmp/infile
    

    ... который записал / tmp / infile , который выглядел так:

    1
    2
    3
    4
    5
    6
    7
    ../a_a_q1.out ../a_a_q2.out ../a_a_q3.out
    9
    10
    11
    12
    13
    14
    
    
    
    

    После запуска приведенного выше фрагмента кода на / tmp , хотя ...

    cat /tmp/infile
    

    1
    2
    3
    4
    5
    6
    7
    ../tmp_q1.out ../tmp_q2.out ../tmp_q3.out
    9
    10
    11
    12
    13
    14
    
    
    
    
8
31.12.2017, 15:28
2 ответа

Чтобы ответить на вопрос в заголовке, вы можете "сдвинуть" массив с помощью нотации подстроки/подмассива. Сам shiftработает только с позиционными параметрами.

$ a=(a b c d e)
$ a=("${a[@]:1}")
$ echo "${a[@]}"
b c d e

Аналогично, чтобы «вытолкнуть» последний элемент из массива:a=("${a[@]:0:${#a[@]} - 1}" )илиunset "a[${#a[@]}-1]"

Так что, если бы вы хотели, вы могли бы сделать это:

a=(foo bar doo)
b=(123 456 789)
while [ "${#a[@]}" -gt 0 ]; do
    echo "$a $b"
    a=("${a[@]:1}")
    b=("${b[@]:1}")
done

Но это портит массивы, а "сдвигающие" присваивания, вероятно, излишне копируют данные, так что обычная индексация может быть лучше .

a=(foo bar doo)
b=(123 456 789)
i=0
while [ "$i" -lt "${#a[@]}" ]; do
    echo "${a[i]} ${b[i]}"
    i=$((i+1))
done

Или, возможно, вместо этого используйте ассоциативный массив, если вас не волнует порядок элементов. "${!arr[@]}"дает ключи в неопределенном порядке, возможно, не в том порядке, в котором они были назначены в:

declare -A arr=([foo]=123 [bar]=456 [doo]=789)
for k in "${!arr[@]}"; do
    echo "$k ${arr[$k]}"
done
27
27.01.2020, 20:08

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

Вот так:

folderArray=( sdb sdc sdd sde sdf sdg  )

counter=0

for i in disk1 disk2 disk3 disk4 disk5 disk6; do
    folder_name=${folderArray[counter++]}
    echo mkdir /data/$folder_name
done

Индекс массива (, который находится внутри []), находится в арифметической среде, поэтому постинкремент(++)работает.


В вопросе о заголовке используется термин shift. Сдвиг, подобный позиционным аргументам, может быть выполнен -повторным построением массива (медленным процессом ).

folderArray=( sdb sdc sdd sde sdf sdg  )

shiftArray=( "${folderArray[@]}" )

counter=0
for i in disk1 disk2 disk3 disk4 disk5 disk6; do
    echo mkdir /data/$shiftArray

    shiftArray=( "${shiftArray[@]:1}" )

done

Но кажется проще использовать индексы массива напрямую:

folderArray=( sdb sdc sdd sde sdf sdg  )

for((i=0;i<${#folderArray[@]};)); do
    echo mkdir /data/"${folderArray[i++]}"
done
0
09.05.2021, 06:38

Теги

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