Извлечь параметры перед последним параметром в «$ @» - PullRequest
58 голосов
/ 01 августа 2009

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

#!/bin/bash
# compact - archive and compact file/folder(s)

eval LAST=\$$#

FILES="$@"
NAME=$LAST

# Usage - display usage if no parameters are given
if [[ -z $NAME ]]; then
  echo "compact <file> <folder>... <compressed-name>.tar.gz"
  exit
fi

# Check if an archive name has been given
if [[ -f $NAME ]]; then
  echo "File exists or you forgot to enter a filename.  Exiting."
  exit
fi

tar -czvpf "$NAME".tar.gz $FILES

Поскольку первые параметры могут иметь любое число, я должен найти способ извлечь последний параметр (например, компактный файл. Файл. Файл. Файл. Как сейчас, имя архива будет включено в файлы для сжатия. Есть ли способ сделать это?

Ответы [ 15 ]

89 голосов
/ 01 августа 2009

Чтобы удалить последний элемент из массива, вы можете использовать что-то вроде этого:

#!/bin/bash

length=$(($#-1))
array=${@:1:$length}
echo $array

Еще короче:

array=${@:1:$#-1}

Но массивы - это Bashism , попробуйте не использовать их: (.

19 голосов
/ 01 августа 2009
last_arg="${!#}" 
11 голосов
/ 02 августа 2009

Несколько решений уже опубликованы; однако я бы посоветовал реструктурировать ваш скрипт так, чтобы имя архива было параметром first , а не последним. Тогда это действительно просто, поскольку вы можете использовать встроенную shift для удаления первого параметра:

ARCHIVENAME="$1"
shift
# Now "$@" contains all of the arguments except for the first
10 голосов
/ 22 октября 2015

Портативные и компактные решения

Вот как я это делаю в своих скриптах

last=${@:$#} # last parameter 
other=${*%${!#}} # all parameters except the last

EDIT
Согласно некоторым комментариям (см. Ниже), это решение более переносимо, чем другие.
Пожалуйста, прочитайте комментарий Майкла Диммитта для объяснения того, как это работает.

5 голосов
/ 02 августа 2009

Спасибо, ребята, сделали это, вот последний скрипт bash:

#!/bin/bash
# compact - archive and compress file/folder(s)

# Extract archive filename for variable
ARCHIVENAME="${!#}"

# Remove archive filename for file/folder list to backup
length=$(($#-1))
FILES=${@:1:$length} 

# Usage - display usage if no parameters are given
if [[ -z $@ ]]; then
  echo "compact <file> <folder>... <compressed-name>.tar.gz"
  exit
fi

# Tar the files, name archive after last file/folder if no name given
if [[ ! -f $ARCHIVENAME ]]; then
  tar -czvpf "$ARCHIVENAME".tar.gz $FILES; else
  tar -czvpf "$ARCHIVENAME".tar.gz "$@"
fi
3 голосов
/ 25 сентября 2017

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

Как сказал @func:

last_arg = "$ {!} #"

Как это работает:

$ {! PARAM} указывает на уровень косвенности. Вы не ссылаетесь на PARAM , а на значение, сохраненное в PARAM (представьте PARAM как указатель на значение).
$ {#} расширяется до количества параметров (Примечание: $ 0 - имя скрипта - здесь не учитывается).

Рассмотрим следующее выполнение:

$./myscript.sh p1 p2 p3

А в myscript.sh

#!/bin/bash

echo "Number of params: ${#}"  # 3
echo "Last parameter using '\${!#}': ${!#}"  # p3
echo "Last parameter by evaluating positional value: $(eval LASTP='$'${#} ; echo $LASTP)"  # p3

Следовательно, вы можете думать о $ {! #} как о ярлыке для вышеуказанного использования eval, что в точности соответствует описанному выше подходу - оценивает значение, сохраненное в данном параметре, здесь параметр равен 3 и содержит позиционный аргумент $ 3

Теперь, если вам нужны все параметры, кроме последнего, вы можете использовать удаление подстроки $ {PARAM% PATTERN} , где знак % означает 'удалить самое короткое соответствие шаблон с конца строки '.

Следовательно, в нашем сценарии:

echo "Every parameter except the last one: ${*%${!#}}"


Вы можете прочитать что-то здесь: Расширение параметра

3 голосов
/ 06 августа 2011

Просто сбросив переменную length, использованную в решении Кшиштофа Климонды:

(
set -- 1 2 3 4 5
echo "${@:1:($#-1)}"       # 1 2 3 4
echo "${@:(-$#):($#-1)}"   # 1 2 3 4
)
1 голос
/ 20 января 2019

Альтернативный способ извлечь последний параметр из списка аргументов:

eval last="\$$#"
eval set -- `awk 'BEGIN{for(i=1;i<'$#';i++) printf " \"$%d\"",i;}'`
1 голос
/ 24 апреля 2018

Массив без последнего параметра:

array=${@:1:$#-1}

Но это bashism :(. Правильные решения будут включать сдвиг и добавление в переменную, как это используют другие.

1 голос
/ 06 августа 2011

Вы уверены, что этот необычный скрипт лучше простого псевдонима tar?

alias compact="tar -czvpf"

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

compact ARCHIVENAME FILES...

Где ФАЙЛЫ могут быть file1 file2 или глобусы типа *.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...