Извлечение параметров перед последним параметром в "$@"
Я пытаюсь создать скрипт, который будет извлекать последний параметр из командной строки в переменную, которая будет использоваться в другом месте. Вот сценарий, над которым я работаю:
#!/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
Так как первые параметры могут быть любого числа, я должен найти способ извлечь последний параметр, (например, компактный файл.папка.файл B.файлы-а-б-д д. тар.ГЗ). Как и сейчас имя архива будет включено в файлы для сжатия. Есть ли способ сделать это?
14 ответов:
удалить последний элемент из массива, вы могли бы использовать что-то вроде этого:
#!/bin/bash length=$(($#-1)) array=${@:1:$length} echo $array
уже опубликовано несколько решений; однако я бы посоветовал перестроить ваш скрипт так, чтобы имя архива было первый
портативные и компактные решения
вот как я делаю в моих скриптах
last=${@:$#} # last parameter other=${*%${!#}} # all parameters except the last
мне это нравится, потому что это очень компактное решение.
Спасибо ребята, сделали это, вот окончательный сценарий Баш:
#!/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
просто сбрасывая
length
переменная, используемая в решении Кшиштофа Климонда:( set -- 1 2 3 4 5 echo "${@:1:($#-1)}" # 1 2 3 4 echo "${@:(-$#):($#-1)}" # 1 2 3 4 )
Я бы добавил Это в качестве комментария, но у меня недостаточно репутации, и ответ все равно стал немного длиннее. Надеюсь, он не возражает.
как заявил @func:
last_arg="${!#}"
как работает:
${!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: ${*%${!#}}"
вы можете прочитать тут: расширение
вы уверены, что этот причудливый скрипт лучше, чем простой псевдоним для tar?
alias compact="tar -czvpf"
использование:
compact ARCHIVENAME FILES...
где файлы могут быть
file1 file2
или капельки как*.html
Я не могу найти способ использовать нотацию массива-индекса на
$@
, Так что это лучшее, что я могу сделать:#!/bin/bash args=("$@") echo "${args[$(($#-1))]}"
этот скрипт может работать для вас-он возвращает поддиапазон аргументов, и может быть вызван из другого скрипта.
примеры его выполнения:
$ args_get_range 2 -2 y a b "c 1" d e f g 'b' 'c 1' 'd' 'e' $ args_get_range 1 2 n arg1 arg2 arg1 arg2 $ args_get_range 2 -2 y arg1 arg2 arg3 "arg 4" arg5 'arg2' 'arg3' $ args_get_range 2 -1 y arg1 arg2 arg3 "arg 4" arg5 'arg2' 'arg3' 'arg 4' # You could use this in another script of course # by calling it like so, which puts all # args except the last one into a new variable # called NEW_ARGS NEW_ARGS=$(args_get_range 1 -1 y "$@")
args_get_range.sh
#!/usr/bin/env bash function show_help() { IT=" Extracts a range of arguments from passed in args and returns them quoted or not quoted. usage: START END QUOTED ARG1 {ARG2} ... e.g. # extract args 2-3 $ args_get_range.sh 2 3 n arg1 arg2 arg3 arg2 arg3 # extract all args from 2 to one before the last argument $ args_get_range.sh 2 -1 n arg1 arg2 arg3 arg4 arg5 arg2 arg3 arg4 # extract all args from 2 to 3, quoting them in the response $ args_get_range.sh 2 3 y arg1 arg2 arg3 arg4 arg5 'arg2' 'arg3' # You could use this in another script of course # by calling it like so, which puts all # args except the last one into a new variable # called NEW_ARGS NEW_ARGS=$(args_get_range.sh 1 -1 \"$@\") " echo "$IT" exit } if [ "" == "help" ] then show_help fi if [ $# -lt 3 ] then show_help fi START= END= QUOTED= shift; shift; shift; if [ $# -eq 0 ] then echo "Please supply a folder name" exit; fi # If end is a negative, it means relative # to the last argument. if [ $END -lt 0 ] then END=$(($#+$END)) fi ARGS="" COUNT=$(($START-1)) for i in "${@:$START}" do COUNT=$((COUNT+1)) if [ "$QUOTED" == "y" ] then ARGS="$ARGS '$i'" else ARGS="$ARGS $i" fi if [ $COUNT -eq $END ] then echo $ARGS exit; fi done echo $ARGS
массив без последнего параметра:
array=${@:1:$#-1}
но это bashism :(. Правильные решения будут включать сдвиг и добавление в переменную, как используют другие.