Вопрос или проблема
У меня есть следующий (MWE) скрипт оболочки foo
:
#!/bin/bash
ARGS=("$@") # все аргументы
## => если он существует, нам нужно удалить аргумент "-D" здесь
ls -l ${ARGS[@]} | sort -fk8
Если foo
вызывается с аргументом -D
(позиция в списке аргументов неизвестна), как я могу удалить -D
из списка аргументов? Я узнал, что unset ARGS[${#ARGS[@]}-1]
может удалить последний аргумент, например, но я не уверен, в каком порядке передаются аргументы (поэтому мне сначала нужно знать, на каком месте находится аргумент, а затем удалить его, если он указан).
Самый простой подход заключается в том, чтобы просто пройтись по позиционным параметрам, собрав все, кроме -D
, в массив, а затем использовать set --
, чтобы обновить параметры:
for param; do
[[ ! $param == '-D' ]] && newparams+=("$param")
done
set -- "${newparams[@]}" # перезаписывает оригинальные позиционные параметры
В zsh
используйте оператор расширения параметров ${array:#pattern}
:
$ set foo -D -D bar '' $'a\nb'
$ printf '<%s>\n' "${@:#-D}"
<foo>
<bar>
<>
<a
b>
Согласно стандарту POSIX:
for i do
[ "$i" = -D ] || set -- "$@" "$i"
shift
done
printf '<%s>\n' "$@"
Кстати, вы забыли кавычки, --
и -d
:
ls -ld -- "$@"
Если вы хотите сортировать по времени изменения, вы можете просто использовать опцию -t
, здесь с -r
(обратно) для наиболее старых:
ls -lrtd -- "$@"
Осторожно: если $ARGS
является пустым массивом, он будет отображать .
. Поэтому вы можете сделать:
[ "$@" -eq 0 ] || ls -lrtd -- "$@"
Чтобы сортировать надежно по часу суток независимо от даты, с zsh
и реализацией ls
, которая поддерживает -U
для несортировки:
zmodload zsh/stat # лучше всего в ~/.zshrc
bytime() zstat -LA REPLY -F%T +mtime -- $REPLY
ls --full-time -ldU -- .(e{'reply=("$@")'}o+bytime)
С ограниченными оболочками, такими как bash
, очень трудно сортировать файлы на основе произвольных метаданных. Снова, если у вас есть доступ к современным инструментам GNU, это немного проще:
[ "$#" -gt 0 ] && (
export LC_ALL=C
printf '%s\0' "$@" |
sed -z 's|^-$|./-|' |
xargs -r0 stat --printf '%y\t%n\0' -- |
sort -zk2,2 |
cut -zf 2-
) | xargs -r0 ls -lUd --
Портативно (но все еще используя нестандартный ls -U
здесь), обычно проще обратиться к perl
, например:
perl -MPOSIX -e '
exec qw{ls -ldU --},
map {$_->[1]}
sort {$a->[0] cmp $b->[0]}
map {[strftime("%T", localtime((lstat$_)[9])), $_]}
@ARGV' -- "$@"
Используйте ${@/-D}
, чтобы удалить -D
из позиционных параметров:
set -- a 1 2 -D -P; set -- ${@/-D}; echo "$*"
a 1 2 -P
с пользовательским массивом:
set -- -D -l foo.txt; ARGS=(${@/-D}); echo "${ARGS[*]}"; ls ${ARGS[*]}
-l foo.txt
-rw-r--r-- 1 j j 0 Dec 18 16:56 foo.txt
Ответ или решение
Чтобы удалить аргумент -D
из списка аргументов в shell-скрипте на Bash, можно воспользоваться несколькими подходами. Наиболее простым и универсальным является использование цикла для обхода всех позиционных параметров и создания нового массива без нежелательного аргумента. В рамках данного ответа будет приведен пошаговый процесс решения задачи.
Пример скрипта
Рассмотрим ваш изначальный скрипт foo
:
#!/bin/bash
ARGS=("$@") # все аргументы
ls -l ${ARGS[@]} | sort -fk8
Шаг 1: Определение нежелательного аргумента
Вам необходимо пройтись по всем элементам массива и отфильтровать аргумент -D
.
Шаг 2: Создание нового массива без -D
Мы можем использовать цикл for
, чтобы собрать все аргументы, кроме -D
, в новый массив:
#!/bin/bash
ARGS=("$@") # все аргументы
newparams=() # инициализируем новый массив
for param in "${ARGS[@]}"; do
[[ "$param" != '-D' ]] && newparams+=("$param")
done
# Обновляем исходные аргументы
set -- "${newparams[@]}"
ls -l "${@}" | sort -fk8
Пояснение кода
- Инициализация массива: Мы сохраняем все переданные аргументы в массив
ARGS
. - Фильтрация: С помощью цикла
for
мы проходим по каждому параметру вARGS
. Условие[[ "$param" != '-D' ]]
позволяет добавить параметр в новый массивnewparams
только если он не равен-D
. - Обновление аргументов: С помощью команды
set -- "${newparams[@]}"
мы обновляем позиционные параметры, чтобы использовать их в дальнейших командах. - Использование отфильтрованных аргументов: Наконец, мы выполняем команду
ls -l
с обновлённым списком аргументов.
Альтернативные методы
Во многих случаях вам может потребоваться более элегантное решение:
- Использование параметров оболочки Bash:
Вы можете воспользоваться встроенной системой замены параметров Bash:
#!/bin/bash
set -- "${@/-D/}" # Удаляем -D
ls -l "$@" | sort -fk8
Однако этот метод не всегда может работать корректно с множественным вхождением аргумента -D
, так как он удалит только первое вхождение. В этом случае стоит остановиться на первом подходе с сохранением аргументов в новом массиве.
Рекомендации
- Не забывайте об экранировании: Всегда используйте кавычки вокруг переменных, чтобы избежать ошибок с пробелами в аргументах.
- Проверяйте аргументы: При отсутствии аргументов стоит обрабатывать ситуацию, чтобы избежать вызова
ls
без параметров. Например, добавьте проверку:
if [ "$#" -gt 0 ]; then
ls -l "$@" | sort -fk8
else
echo "Нет файлов для отображения."
fi
Заключение
Использование цикла для фильтрации аргументов и обновление позиционных параметров через set
— это эффективный способ управления входными данными в Bash-скрипте. Такие методы не только повышают читаемость и устойчивость вашего кода, но и делают его более адаптивным к возможным изменениям в списке аргументов.