Как увеличить номер версии в сценарии оболочки?

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

Тем не менее, одна из основных проблем текущей функциональности заключается в том, как реализовать следующее: если в последнем разделе после точки есть две цифры, включите до 99; если только 1, то прибавляем до 9, потом переходим к предыдущему разделу. Версии могут иметь любое положительное целое число секций.

1.2.3.44 -> 1.2.3.45
1.2.3.9 -> 1.2.4.0
1.2.3 -> 1.2.4
9 -> 10

Оставшаяся проблема заключается в том, что он не ждет, пока редактор вина с вкладками закроет файл; цель состоит в том, чтобы определить, когда вкладка закрыта. Кроме того, не могли бы вы объяснить, как лучше всего убедиться, что мои имена переменных не перезаписывают существующие?

Вы также можете предложить другие улучшения.

#!/bin/bash
#Tested on bash 4.1.5
#All arguments in order: "folder with file" "file pattern" cmd [stable name]
folder="$1"
file_pattern="$2"
cmd="$3"
stable="$4"

cd "$folder"
last_version=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    sed -nr 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' | \
    sort -Vu | \
    tail -n 1)
last_version_file=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    grep $last_version | \
    tail -n 1) #tail -n 1 is only needed to get 1 line if there are backup files with the same version number
new_version=$(echo $last_version | \
    gawk -F"." '{$NF+=1}{print $0RT}' OFS="." ORS="") #increments last section indefinitely
new_version_file=$(echo "$last_version_file" | \
    sed -r "s/$last_version/$new_version/")
cp "$last_version_file" "$new_version_file"
"$cmd" "$new_version_file" & \
    wait #works with gedit but not with wine tabbed editor
[[ "$stable" ]] && \
    cp "$new_version_file" "$stable" #True if the length of string is non-zero.

Обновление: на моем компьютере работает следующее, я обновлю его, если будут найдены улучшения или решения нерешенных проблем:

#!/bin/bash
inc()
{
shopt -s extglob
    num=${last_version//./}
    let num++

    re=${last_version//./)(}
    re=${re//[0-9]/.}')'
    re=${re#*)}

    count=${last_version//[0-9]/}
    count=$(wc -c<<<$count)
    out=''
    for ((i=count-1;i>0;i--)) ; do
        out='.\\'$i$out
    done

    sed -r s/$re$/$out/ <<<$num
}

folder="$1"
file_pattern="$2"
cmd="$3"
stable="$4"

cd "$folder"
last_version=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    sed -nr 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' | \
    sort -Vu | \
    tail -n 1) #--almost-all do not list implied . and ..
last_version_file=$(ls --format=single-column --almost-all | \
    grep "$file_pattern" | \
    grep $last_version | \
    tail -n 1) #tail -n 1 is only needed to get 1 line if there are backup files with the same version number
new_version=$(inc)
new_version_file=$(echo "$last_version_file" | \
    sed -r "s/$last_version/$new_version/")
cp "$last_version_file" "$new_version_file"
"$cmd" "$new_version_file" && \
    wait #works with gedit but not tabbed wine editor
[[ "$stable" ]] && \
    cp "$new_version_file" "$stable" #True if the length of string is non-zero.

Я ценю разнообразие предложенных решений, поскольку они помогают составить представление и провести сравнение.


person nnn    schedule 28.12.2011    source источник
comment
я никогда не использовал редактор с вкладками вина, поэтому не могу помочь, но, пожалуйста, уточните, что вы подразумеваете под «... убедитесь, что мои имена переменных не перезаписывают существующие ..» - каков контекст этого ? специальные переменные оболочки или переменные в вашем скрипте?   -  person venzen    schedule 28.12.2011
comment
как насчет 9.9.9? Быть 9.10.0 или 10.0.0?   -  person kev    schedule 28.12.2011
comment
Я имел в виду любой редактор с вкладками, запускаемый Wine, например Notepad Tabs. Что касается папок, я имею в виду, что если человек сам использует $folder, например, для своей команды редактора: alias edit=$folder\Notepad2, тогда он может работать неправильно в этом скрипте.   -  person nnn    schedule 28.12.2011
comment
Я думаю, что 9.9.9 должен перейти на 10.0.0.   -  person nnn    schedule 28.12.2011
comment
@nnn Я обновил, чтобы поддерживать 10.0.0   -  person kev    schedule 28.12.2011
comment
См. stackoverflow.com /вопросы/6245293/   -  person Boris Chervenkov    schedule 05.10.2013
comment
Номера версий обычно не ведут себя как десятичные числа, поэтому 9.9.9 обычно увеличивается до 9.9.10. Также 0,100 > 0,50 > 0,10 > 0,5 > 0,1.   -  person Sean Burton    schedule 07.11.2013
comment
имея скрипт, увеличивающий номер версии - вам нужно определить, какая версия должна быть увеличена: основная, второстепенная, обслуживание и / или сборка - как уже писал @SeanBurton, вы не можете просто увеличить номер версии, например десятичные числа, вы можете перейти от 1.2 -> 2.0 или с 1.12 -> 1.13 или с 2.10 -> 2.10.1   -  person Cybot    schedule 21.09.2015
comment
Я написал этот скрипт, который автоматизирует множество предварительных шагов, в том числе функциональность бамп-версии, надеюсь, он поможет всем, кто хочет выполнять такие шаги автоматически github.com/jv-k/bump-version.sh   -  person jv-k    schedule 23.11.2020


Ответы (11)


$ echo 1.2.3.4 | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{if(length($NF+1)>length($NF))$(NF-1)++; $NF=sprintf("%0*d", length($NF), ($NF+1)%(10^length($NF))); print}'
1.2.3.5

1.2.3.9  => 1.2.4.0
1.2.3.44 => 1.2.3.45
1.2.3.99 => 1.2.4.00
1.2.3.999=> 1.2.4.000
1.2.9    => 1.3.0
999      => 1000

ОБНОВИТЬ:

#!/usr/bin/gawk -f

BEGIN{
    v[1] = "1.2.3.4"
    v[2] = "1.2.3.44"
    v[3] = "1.2.3.99"
    v[4] = "1.2.3"
    v[5] = "9"
    v[6] = "9.9.9.9"
    v[7] = "99.99.99.99"
    v[8] = "99.0.99.99"
    v[9] = ""

    for(i in v)
        printf("#%d: %s => %s\n", i, v[i], inc(v[i])) | "sort | column -t"
}

function inc(s,    a, len1, len2, len3, head, tail)
{
    split(s, a, ".")

    len1 = length(a)
    if(len1==0)
        return -1
    else if(len1==1)
        return s+1

    len2 = length(a[len1])
    len3 = length(a[len1]+1)

    head = join(a, 1, len1-1)
    tail = sprintf("%0*d", len2, (a[len1]+1)%(10^len2))

    if(len2==len3)
        return head "." tail
    else
        return inc(head) "." tail
}

function join(a, x, y,    s)
{
    for(i=x; i<y; i++)
        s = s a[i] "."
    return s a[y]
}

$ chmod +x inc.awk
$ ./inc.awk
#1:  1.2.3.4      =>  1.2.3.5
#2:  1.2.3.44     =>  1.2.3.45
#3:  1.2.3.99     =>  1.2.4.00
#4:  1.2.3        =>  1.2.4
#5:  9            =>  10
#6:  9.9.9.9      =>  10.0.0.0
#7:  99.99.99.99  =>  100.00.00.00
#8:  99.0.99.99   =>  99.1.00.00
#9:  =>           -1
person kev    schedule 28.12.2011
comment
уй! почему я игнорировал awk все эти годы? - person venzen; 28.12.2011
comment
Я пытался протестировать вашу обновленную версию, но она зависает на моем компьютере и ничего не печатает. - person nnn; 29.12.2011
comment
@nnn Попробуйте удалить a, из function inc(s, a, len1, len2, len3, head, tail). Можете ли вы опубликовать сообщение об ошибке? - person kev; 29.12.2011
comment
@nnn Ваша версия awk слишком старая. - person kev; 29.12.2011
comment
Не работает для 00.01.99 => 00.2.00 (и 1.02.00 с использованием обновленного скрипта). Я ожидал 00.02.00. - person Kevin Brotcke; 03.04.2015
comment
Для тех, кто хочет использовать стандартный шаг, 9.9.9 => 9.9.10 используйте этот код echo 9.9.9.9 | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{$NF=sprintf("%0*d", length($NF), ($NF+1)); print}' - person Sam; 02.09.2016
comment
Мне нравится ваша однострочная версия, но она не работает с моей версией awk (и я не могу легко ее обновить) awk: cmd. line:1: %*x formats are not supported Знаете ли вы, как я могу обойти эту ошибку формата, сохранив то же поведение? Это кажется возможным в отношении документации awk, но я могу не заставляйте его работать с вашим решением. - person Nicolas Pepinster; 29.01.2018
comment
тоже столкнулся с этим - awk: cmd. строка: 1: форматы %*x не поддерживаются; используйте NEXTVERSION=$(echo ${VERSION} | awk -F. -v OFS=. '{$NF++;print}') - person Sage; 10.05.2021

Вот еще несколько гибких вариантов. Оба принимают второй аргумент, чтобы указать, какую позицию увеличивать.

1. Простая функция

Для более предсказуемого ввода.

# Usage: increment_version <version> [<position>]
increment_version() {
 local v=$1
 if [ -z $2 ]; then 
    local rgx='^((?:[0-9]+\.)*)([0-9]+)($)'
 else 
    local rgx='^((?:[0-9]+\.){'$(($2-1))'})([0-9]+)(\.|$)'
    for (( p=`grep -o "\."<<<".$v"|wc -l`; p<$2; p++)); do 
       v+=.0; done; fi
 val=`echo -e "$v" | perl -pe 's/^.*'$rgx'.*$/$2/'`
 echo "$v" | perl -pe s/$rgx.*$'/${1}'`printf %0${#val}s $(($val+1))`/
}

# EXAMPLE   ------------->   # RESULT
increment_version 1          # 2
increment_version 1.0.0      # 1.0.1
increment_version 1 2        # 1.1
increment_version 1.1.1 2    # 1.2
increment_version 00.00.001  # 00.00.002

2. Надежная функция

Для использования со сценариями или дополнительной настройки для применения к различным системам управления версиями. Он мог бы использовать еще пару вариантов, но в его нынешнем виде он работает для моих проектов, использующих последовательности версий «major.minor[.maintenance[.build]]».

# Accepts a version string and prints it incremented by one.
# Usage: increment_version <version> [<position>] [<leftmost>]
increment_version() {
   local usage=" USAGE: $FUNCNAME [-l] [-t] <version> [<position>] [<leftmost>]
           -l : remove leading zeros
           -t : drop trailing zeros
    <version> : The version string.
   <position> : Optional. The position (starting with one) of the number 
                within <version> to increment.  If the position does not 
                exist, it will be created.  Defaults to last position.
   <leftmost> : The leftmost position that can be incremented.  If does not
                exist, position will be created.  This right-padding will
                occur even to right of <position>, unless passed the -t flag."

   # Get flags.
   local flag_remove_leading_zeros=0
   local flag_drop_trailing_zeros=0
   while [ "${1:0:1}" == "-" ]; do
      if [ "$1" == "--" ]; then shift; break
      elif [ "$1" == "-l" ]; then flag_remove_leading_zeros=1
      elif [ "$1" == "-t" ]; then flag_drop_trailing_zeros=1
      else echo -e "Invalid flag: ${1}\n$usage"; return 1; fi
      shift; done

   # Get arguments.
   if [ ${#@} -lt 1 ]; then echo "$usage"; return 1; fi
   local v="${1}"             # version string
   local targetPos=${2-last}  # target position
   local minPos=${3-${2-0}}   # minimum position

   # Split version string into array using its periods. 
   local IFSbak; IFSbak=IFS; IFS='.' # IFS restored at end of func to                     
   read -ra v <<< "$v"               #  avoid breaking other scripts.

   # Determine target position.
   if [ "${targetPos}" == "last" ]; then 
      if [ "${minPos}" == "last" ]; then minPos=0; fi
      targetPos=$((${#v[@]}>${minPos}?${#v[@]}:$minPos)); fi
   if [[ ! ${targetPos} -gt 0 ]]; then
      echo -e "Invalid position: '$targetPos'\n$usage"; return 1; fi
   (( targetPos--  )) || true # offset to match array index

   # Make sure minPosition exists.
   while [ ${#v[@]} -lt ${minPos} ]; do v+=("0"); done;

   # Increment target position.
   v[$targetPos]=`printf %0${#v[$targetPos]}d $((10#${v[$targetPos]}+1))`;

   # Remove leading zeros, if -l flag passed.
   if [ $flag_remove_leading_zeros == 1 ]; then
      for (( pos=0; $pos<${#v[@]}; pos++ )); do
         v[$pos]=$((${v[$pos]}*1)); done; fi

   # If targetPosition was not at end of array, reset following positions to
   #   zero (or remove them if -t flag was passed).
   if [[ ${flag_drop_trailing_zeros} -eq "1" ]]; then
        for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do unset v[$p]; done
   else for (( p=$((${#v[@]}-1)); $p>$targetPos; p-- )); do v[$p]=0; done; fi

   echo "${v[*]}"
   IFS=IFSbak
   return 0
}

# EXAMPLE   ------------->   # RESULT
increment_version 1          # 2
increment_version 1 2        # 1.1
increment_version 1 3        # 1.0.1
increment_version 1.0.0      # 1.0.1
increment_version 1.2.3.9    # 1.2.3.10
increment_version 00.00.001  # 00.00.002
increment_version -l 00.001  # 0.2
increment_version 1.1.1.1 2   # 1.2.0.0
increment_version -t 1.1.1 2  # 1.2
increment_version v1.1.3      # v1.1.4
increment_version 1.2.9 2 4     # 1.3.0.0
increment_version -t 1.2.9 2 4  # 1.3
increment_version 1.2.9 last 4  # 1.2.9.1

Очевидно, что это излишне только для увеличения строки версии. Но я написал это, потому что мне нужны были разные типы проектов, и потому что, если скорость не является проблемой, я предпочитаю повторное использование, а не настройку одного и того же кода в десятках скриптов. Я предполагаю, что это просто моя объектно-ориентированная сторона просачивается в мои сценарии.

person Stephen M. Harris    schedule 28.06.2013
comment
Большой поклонник этого ответа, одна вещь заключается в том, что в строке 49 «Увеличить целевую позицию» он увеличивается в неправильной базе, поэтому он не может увеличиваться, когда версия равна 08. Исправить просто, измените строку на следующее: v[$targetPos]=`printf %0${#v[$targetPos]}d $((10#${v[$targetPos]}+1))`; - person William King; 23.10.2016

Вот еще более короткая версия, которая также поддерживает постфикс (хорошо для -SNAPSHOT)

$ cat versions
1.2.3.44
1.2.3.9
1.2.3
9
42.2-includes-postfix

$ perl -pe 's/^((\d+\.)*)(\d+)(.*)$/$1.($3+1).$4/e' < versions
1.2.3.45
1.2.3.10
1.2.4
10
42.3-includes-postfix

Объяснение

Я использовал регулярное выражение для захвата 3 частей. Материал перед последней позицией, число для увеличения и материал после.

  • ((\d+\.)*) - материал из 1.1.1.1.1.
  • (\d+) - последняя цифра
  • (.*) - материал после последней цифры

Затем я использую параметр e, чтобы разрешить выражения в части замены. Обратите внимание, что с опцией e \1 становится переменной $1, и вам необходимо объединить переменные с помощью оператора точки.

  • $1 - группа захвата 1.1.1.1.1.
  • ($3+1) - увеличить последнюю цифру. примечание $2 используется в подгруппе $1 для получения повторяющейся 1.
  • $4 - материал после последней цифры
person Pyrolistical    schedule 01.02.2014
comment
Примечание 1.2.3.9 становится 1.2.3.10, а НЕ 1.2.4.0, как того требует вопрос. Я думаю, что это фиктивное требование, и никто никогда не хочет этого при создании версий. - person Pyrolistical; 01.02.2014

Для простого увеличения десятичной версии Дьюи: awk -F. -v OFS=. '{$NF++;print}'

Или в сценарии оболочки:

NEXTVERSION=$(echo ${VERSION} | awk -F. -v OFS=. '{$NF++;print}')
person Chris Jones    schedule 20.05.2020
comment
Для меня это лучшее решение, так как мне не нужно, чтобы оно было полностью надежным или настраиваемым. - person Pureferret; 30.10.2020
comment
Это должен быть ответ! - person HeroFromEarth; 02.03.2021

Чистый Баш:

increment_version ()
{
  declare -a part=( ${1//\./ } )
  declare    new
  declare -i carry=1

  for (( CNTR=${#part[@]}-1; CNTR>=0; CNTR-=1 )); do
    len=${#part[CNTR]}
    new=$((part[CNTR]+carry))
    [ ${#new} -gt $len ] && carry=1 || carry=0
    [ $CNTR -gt 0 ] && part[CNTR]=${new: -len} || part[CNTR]=${new}
  done
  new="${part[*]}"
  echo -e "${new// /.}"
} 

version='1.2.3.44'

increment_version $version

результат:

1.2.3.45

Строка версии разделяется и сохраняется в массиве part. Цикл идет от последней к первой части версии. Последняя часть будет увеличена и, возможно, сокращена до исходной длины. Перенос осуществляется в следующую часть.

person Fritz G. Mehner    schedule 28.12.2011
comment
На моем компьютере он печатает пробелы вместо точек, но в остальном работает правильно. + есть объяснение. - person nnn; 29.12.2011
comment
Исправлена ​​проблема, упомянутая в комментарии nnn. - person Fritz G. Mehner; 29.12.2011
comment
@fgm, если мы используем 1.2.9.9, то он показывает 1.2.10.0, должно быть 1.3.0.0 - person damithH; 22.07.2014
comment
@damithH У меня это работает правильно. Проблема с версией Bash/shell? - person Fritz G. Mehner; 23.07.2014

1. Увеличивайте только выбранную часть

использование

increment_version 1.39.0 0 # 2.39.0
increment_version 1.39.0 1 # 1.40.0
increment_version 1.39.0 2 # 1.39.1

Код

### Increments the part of the string
## $1: version itself
## $2: number of part: 0 – major, 1 – minor, 2 – patch
increment_version() {
  local delimiter=.
  local array=($(echo "$1" | tr $delimiter '\n'))
  array[$2]=$((array[$2]+1))
  echo $(local IFS=$delimiter ; echo "${array[*]}")
}

Упрощенная версия ответа @dimpiax


РЕДАКТИРОВАТЬ: я создал еще одну версию этого скрипта, которая обнуляет менее важные части, если самые важные из них изменены. Просто обратите внимание на различные ожидаемые результаты в части использования.

2. Увеличить выбранную часть и поставить нули на последующие части

использование

increment_version 1.39.3 0 # 2.0.0
increment_version 1.39.3 1 # 1.40.0
increment_version 1.39.3 2 # 1.39.4
#!/bin/bash

### Increments the part of the string
## $1: version itself
## $2: number of part: 0 – major, 1 – minor, 2 – patch

increment_version() {
  local delimiter=.
  local array=($(echo "$1" | tr $delimiter '\n'))
  array[$2]=$((array[$2]+1))
  if [ $2 -lt 2 ]; then array[2]=0; fi
  if [ $2 -lt 1 ]; then array[1]=0; fi
  echo $(local IFS=$delimiter ; echo "${array[*]}")
}
person Alan Rabello    schedule 16.10.2020
comment
Вау, это лучшее решение !! - person Bruno Adelé; 10.01.2021
comment
Привет, похоже, отличное решение, я пытаюсь получить вывод в var, однако он ничего не возвращает при запуске в терминале - что я делаю неправильно :-/ ./increment.sh increment_version 1.39.0 0 - person einonsy; 17.01.2021
comment
@einonsy, если вы поместите функцию отдельно внутри increment.sh, вы должны ее вызвать. Для простоты вы можете просто поместить вызов в последнюю строку того же скрипта, например: increment_version $1 $2 Или вы можете удалить функцию и просто запустить ее. Для этого вы должны удалить все вхождения строки local, так как они должны быть только внутри функций. - person Alan Rabello; 20.01.2021

Устали от Баша? Почему бы не попробовать Perl?

$ cat versions
1.2.3.44
1.2.3.9
1.2.3
9

$ cat versions | perl -ne 'chomp; print join(".", splice(@{[split/\./,$_]}, 0, -1), map {++$_} pop @{[split/\./,$_]}), "\n";'
1.2.3.45
1.2.3.10
1.2.4
10

Не совсем в соответствии с требованием, конечно.

person sorpigal    schedule 29.12.2011
comment
(то есть: <versions perl -ne ... позволит perl читать прямо из файла, а не из FIFO, прикрепленного к копии cat, которая не добавляет ничего, кроме накладных расходов). - person Charles Duffy; 26.06.2017

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

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

"...если в последней секции после точки две цифры, прибавлять до 99; если только 1, то прибавлять до 9..."

Предполагая, что третья позиция — это номер стадии разработки $dNum, а четвертая (последняя) позиция — это номер версии $rNum:

if  [ $(expr length $rNum) = "2" ] ; then 
    if [ $rNum -lt 99 ]; then 
        rNum=$(($rNum + 1))
    else rNum=0
         dNum=$(($dNum + 1)) #some additional logic for $dNum > 9 also needed
    fi
elif [ $(expr length $dNum) = "1" ] ; then
    ...
    ...
fi

Возможно, функция позволит наиболее лаконично обрабатывать все позиции (majNum.minNum.dNum.rNum).

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

new_version="$majNum.minNum.$dNum.$rNum"
new_version_file="$old_file.$new_version"

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

person venzen    schedule 28.12.2011

Другой вариант — использовать Python. Я думаю, что таким образом это немного более читабельно, чем использование простого Bash или Perl.

function increase_version() {
    python - "$1" <<EOF
import sys
version = sys.argv[1]
base, _, minor = version.rpartition('.')
print(base + '.' + str(int(minor) + 1))
EOF
}
person kokosing    schedule 13.03.2019

использование

increment_version 1.39.0 0 # 2.39.0
increment_version 1.39.0 1 # 1.40.0
increment_version 1.39.0 2 # 1.39.1

Код

### Increments the part of the string
## $1: version itself
## $2: number of part: 0 – major, 1 – minor, 2 – patch
increment_version() {
  local delimiter=.
  local array=($(echo "$1" | tr $delimiter '\n'))

  for index in ${!array[@]}; do
    if [ $index -eq $2 ]; then
      local value=array[$index]
      value=$((value+1))
      array[$index]=$value
      break
    fi
  done

  echo $(IFS=$delimiter ; echo "${array[*]}")
}
person dimpiax    schedule 13.08.2020

Используя только bash, wc и sed:

#! /bin/bash
for v in 1.2.3.44 1.2.3.9 1.2.3 9 1.4.29.9 9.99.9 ; do
    echo -n $v '-> '

    num=${v//./}
    let num++

    re=${v//./)(}
    re=${re//[0-9]/.}')'
    re=${re#*)}

    count=${v//[0-9]/}
    count=$(wc -c<<<$count)
    out=''
    for ((i=count-1;i>0;i--)) ; do
        out='.\'$i$out
    done

    sed -r s/$re$/$out/ <<<$num
done
person choroba    schedule 28.12.2011
comment
жалуется на отсутствие цитирования и echo -n, где printf '%s -> ' "$v" было бы более переносимым вариантом - person Charles Duffy; 26.06.2017