Ответ 1
Чтобы удалить последний элемент из массива, вы можете использовать что-то вроде этого:
#!/bin/bash
length=$(($#-1))
array=${@:1:$length}
echo $array
Я пытаюсь создать Bash script, который будет извлекать последний параметр из командной строки в переменную, которая будет использоваться в другом месте. Здесь script Я работаю над:
#!/bin/bash
# compact - archive and compact file/folder(s)
eval LAST=\$$#
FILES="[email protected]"
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
Так как первые параметры могут быть любого числа, я должен найти способ извлечь последний параметр (например, файл file.b file.b.d.gar). Так как теперь имя архива будет включено в файлы для компактности. Есть ли способ сделать это?
Чтобы удалить последний элемент из массива, вы можете использовать что-то вроде этого:
#!/bin/bash
length=$(($#-1))
array=${@:1:$length}
echo $array
last_arg="${!#}"
Несколько решений уже опубликованы; однако я бы посоветовал реструктурировать ваш script, чтобы имя архива было первым параметром, а не последним. Тогда это действительно просто, так как вы можете использовать shift
встроенный, чтобы удалить первый параметр:
ARCHIVENAME="$1"
shift
# Now "[email protected]" contains all of the arguments except for the first
Вот как я делаю в своих сценариях
last=${@:$#} # last parameter
other=${*%${!#}} # all parameters except the last
РЕДАКТИРОВАТЬ
Согласно некоторым комментариям (см. Ниже), это решение является более портативным, чем другие.
Пожалуйста, прочитайте комментарий Майкла Диммитта для объяснения того, как это работает.
Спасибо, ребята, все сделано, heres final bash script:
#!/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 [email protected] ]]; 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 "[email protected]"
fi
Просто отбросив переменную length
, используемую в решении Krzysztof Klimonda:
(
set -- 1 2 3 4 5
echo "${@:1:($#-1)}" # 1 2 3 4
echo "${@:(-$#):($#-1)}" # 1 2 3 4
)
Я бы добавил это как комментарий, но не имел достаточной репутации, и ответ получил немного больше в любом случае. Надеюсь, что это не против.
Как сказал @func:
last_arg = "$ {!} #"
Как это работает:
${! PARAM} указывает уровень косвенности. Вы не ссылаетесь на PARAM самостоятельно, а на значение, хранящееся в PARAM (считайте PARAM как указатель на значение).
${#} расширяется до количества параметров (Примечание: $0 - имя script - здесь не учитывается).
Рассмотрим следующее выполнение:
$./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 и содержит аргумент position $3
Теперь, если вам нужны все параметры, кроме последнего, вы можете использовать удаление подстроки ${PARAM% PATTERN}, где знак% означает "удалить кратчайший шаблон соответствия от конца строки",
Следовательно, в нашем script:
echo "Every parameter except the last one: ${*%${!#}}"
Здесь вы можете прочитать что-то: Расширение параметров
#!/bin/bash
lastidx=$#
lastidx=`expr $lastidx - 1`
eval last='$'{$lastidx}
echo $last
Try:
if [ "$#" -gt '0' ]; then
/bin/echo "${!#}" "${@:1:$(($# - 1))}
fi
Вы уверены, что этот причудливый script не лучше простого псевдонима для tar?
alias compact="tar -czvpf"
Использование:
compact ARCHIVENAME FILES...
Где FILES может быть file1 file2
или globs как *.html
#!/bin/sh eval last='$'$# while test $# -gt 1; do list="$list $1" shift done echo $list $last
Я не могу найти способ использовать нотацию на основе массива на [email protected]
, так что это лучшее, что я могу сделать:
#!/bin/bash
args=("[email protected]")
echo "${args[$(($#-1))]}"
Этот script может работать для вас - он возвращает поддиапазон аргументов и может быть вызван из другого script.
$ 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 "[email protected]")
#!/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 \"\[email protected]\")
"
echo "$IT"
exit
}
if [ "$1" == "help" ]
then
show_help
fi
if [ $# -lt 3 ]
then
show_help
fi
START=$1
END=$2
QUOTED=$3
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 :(. Правильные решения будут включать сдвиг и добавление в переменную, как используют другие.
Альтернативный способ извлечь последний параметр из списка аргументов:
eval last="\$$#"
eval set -- 'awk 'BEGIN{for(i=1;i<'$#';i++) printf " \"$%d\"",i;}''