Bash: встроенная команда shift |
![]() |
Добавил(а) microsin | ||
На операционных системах семейства Unix (Linux, FreeBSD и т. п.) в скриптах шелла bash работает команда shift. Когда она выполняется, происходит сдвиг влево позициональных параметров скрипта (т. е. аргументов командной строки, переданных в скрипт). Каждый из параметров помещается в более младшую относительно текущей позицию. [Описание команды shift] Когда вы запускаете shift n, текущие позициональные параметры сдвигаются влево n раз. Позициональный параметр x получает значение позиционального параметра x+n. Если параметр x+n не существует, то параметр x становится неустановленным. Если величина сдвига n для команды shift не указана, то применяется значение по умолчанию 1. Таким образом, команды shift 1 и shift без аргумента делают одно и то же. Если параметр сдвигается в позицию, которая меньше 1, то он выбрасывается, т. е. его значение теряется. Так что, к примеру, команда shift будет всегда выбрасывать предыдущее значение $1, и shift 2 всегда выбросит предыдущие значения $1 и $2. Специальный позициональный параметр $0 исключается из операций shift, и он всегда остается доступным, на него команда shift не влияет. Синтаксис команды shift: shift [n] Параметры. Команда shift принимает только 1 аргумент:
n Количество позиций, на которое параметры должны быть сдвинуты влево. Это значение может быть любым не отрицательным целым числом. Если для n указан ноль (0), то никакой сдвиг параметров не производится. Значение по умолчанию для n (если параметр n не указан) равно 1. Возвращаемый статус: при успешном завершении команды shift она возвратит значение 0 (что соответствует отсутствию ошибок). В противном случае, если n отрицательное, или n больше, чем текущее количество позициональных параметров, то будет возвращено ненулевое значение. [Примеры команды shift] Создадим простой скрипт, который принимает аргументы. Так мы на практике увидим, как аргументы сохраняются в окружении в виде позициональных параметров, и как shift на них влияет. Для создания скрипта используйте любой редактор, например vim, gedit или mcedit, после чего с помощью команды chmod u+x имяскрипта поменяйте его атрибуты, разрещающие запуск. #!/bin/bash
shift 0
echo 0: $0 echo 1: $1 echo 2: $2 echo 3: $3 echo 4: $4 Первая строка скрипта содержит "магическую" строчку #!/bin/bash, которая определяет интерпретатор скрипта (программу шелла), который должен быть использован для обработки команд скрипта. Вторая строка shift 0 пока ничего не делает. Позже мы поменяем 0 на другое значение, чтобы понять, как работает команда shift. Предположим, что мы создали новый файл скрипта с именем myargs.sh. Сделаем его исполняемым: $ chmod u+x ./myargs.sh
После этого запустим, указав для него пять аргументов: $ ./myargs.sh one two three four five
0: ./myargs.sh
1: one
2: two
3: three
4: four
В этом выводе видны значения позициональных параметров 0 .. 4. Значение параметра $5 равно "five", однако в скрипте оно не используется. Указанные в команде параметры относятся только к текущей запускаемой команде - нашему скрипту myargs.sh. После завершения команды (скрипта) они теряются, вернее откатываются к своему изначальному (в нашем примере пустому) значению, которое было до запуска команды. Вы можете запустить команду: $ echo $0, $1, $2, $3, $4.
/bin/bash, , , , .
Здесь отображены значение переменных вашей текущей сессии bash. $0 это место размещения исполняемого кода bash, а у остальных параметров $1 .. $4 значения отсутствуют. Давайте теперь поменяем shift 0 на shift 1: #!/bin/bash
shift # то же самое, что shift 1 echo 1: $1 echo 2: $2 echo 3: $3 echo 4: $4 Примечание: все, что указано после символа "#", считается комментарием, и игнорируется при запуске скрипта. Теперь при запуске той же самой команды мы увидим следующее: $ ./myargs.sh one two three four five
0: ./myargs.sh
1: two
2: three
3: four
4: five
Обратите внимание, что позиция всех параметров была сдвинута на 1. Оригинальное значение $1 было выброшено, и значение $5 теперь стало значением $4. Значение $0 не поменялось. Однако простой сдвиг на единицу не особо полезен. Обычно shift запускают несколько раз в цикле, что удобно для выполнения одинаковых повторяющихся действий над параметрами (мы это рассмотрим далее на практическом примере). Для цикла скрипт можно переписать следующим образом: #!/bin/bash
for (( i = 0; i < = 4; i++ )); do
echo Shifted $i time(s): echo ----------------- echo 1: $1 echo 2: $2 echo 3: $3 echo 4: $4 echo shift done
Обратите внимание, что здесь shift это последняя команда в теле цикла. Это позволяет выполнить какую-либо обработку со всеми начальными параметрами до того, как будет произведен самый первый сдвиг. Запустим наш скрипт снова. Он прокрутится 5 раз (i будет последовательно увеличиваться от 0 до 4). С каждой итерацией тела цикла shift будет делать сдвиг позициональных параметров влево, с выводом в терминал значений переменных $1 .. $4. $ ./myargs.sh one two three four five
Shifted 0 time(s):
------------------
1: one
2: two
3: three
4: four
Shifted 1 time(s):
------------------
1: two
2: three
3: four
4: five
Shifted 2 time(s):
------------------
1: three
2: four
3: five
4:
Shifted 3 time(s):
------------------
1: four
2: five
3:
4:
Shifted 4 time(s):
------------------
1: five
2:
3:
4:
Практический пример использования shift. Следующий скрипт clean-old-files.sh принимает в командной строке список имен директорий. Он сканирует каждую из них, проверяя дату модификации находящихся там файлов. Если к каким-либо файлам не было обращения больше одного года, то они удаляются. #!/bin/bash
# Сканирование директорий на наличие старых файлов (к которым не было обращения
# более 365 дней) и удаление их.
USAGE="Usage: $0 dir1 dir2 dir3 ..." if [ "$#" == "0" ]; then # Если не было предоставлено аргументов, echo "Error: no directory names provided." echo "$USAGE" # то выводится текст подсказки exit 1 # и возвращается статус ошибки. fi
while (( "$#" )); do # цикл, в котором происходит сдвиг аргументов while IFS= read -r -d $'\0' file; do echo "Removing $file..." rm $file done < <(find "$1" -type f -atime +365 -print0) shift done
echo "Done." exit 0
Давайте рассмотрим части этого скрипта, чтобы понять, что они делают. USAGE="Usage: $0 dir1 dir2 dir3 ..." if [ "$#" == "0" ]; then echo "Error: no directory names provided." echo "$USAGE" exit 1 fi
В этом операторе проверки условия if скрипт проверяет, указаны ли какие-либо аргументы. Проверка использует нотацию одиночных скобок [ ... ], что эквивалентно встроенной команде test. Специальная переменная окружения $# содержит общее количество позициональных параметров. Если её значение равно 0, то это значит, что пользователь не предоставил для скрипта ни одной директории, в этом случае будет выведен текст подсказки, и скрипт возвратит результат ошибки (exit-статус 1). while (( "$#" )); do Иначе запустится цикл while/do, в его условии применяется проверка условия с двойными круглыми скобками, чтобы проверить, соответствует ли $# значение true (это любое значение, не равне 0). Каждый раз перед запуском прокрутки тела цикла, если $# равно 0, то выражение (( "$#" )) будет вычислено как false, и произойдет выход из цикла. Примечание: выражения (( "$#" )) и [ "$#" != "0" ] дают одинаковые результаты, и функционально взаимозаменяемые. Рассмотрим теперь внутренний цикл while: while IFS= read -r -d $'\0' file; do Эта строка говорит следующее: выполнять, пока (while) имеются раздельные читаемые элементы (-d), отделенные друг от друга нулевым символом ( $'\0' ), с чтением такого элемента в переменную file, после чего выполнить команды внутри цикла (do). Элементы предоставляются строкой в теле цикла: done < <(find "$1" -type f -atime +365 -print0) Эта строка говорит: найти все файлы, начинать искать в директории dir1 (позициональный параметр $1), к которой был доступ последний раз (-atime) больше чем ( + ) 365 дней тому назад. Разделить список найденных файлов нулевыми символами null (-print0). Команда поиска find обернута в <( ... ), что использует перенаправление, обрабатывающее вывод как если бы это был файл. Содержимое "файла" перенаправляется ( < ) во внутренний цикл while. Здесь read интерпретирует все до null-символа как имя следующего файла, и назначает это имя переменной file. Затем выполняется тело внутреннего цикла, которое печатает имя удаляемого файла и удаляет его: echo "Removing $file..." rm $file Внутренний цикл продолжается до тех пор, пока имена файлов не закончаться, тогда read вернет false. Произойдет выход из внутреннего цикла, где выполняется: shift
Команда shift сдвинет позициональные параметры, так что dir2 ($2) станет теперь dir1 ($1). Старое значение $1 при сдвиге выбрасывается, и значение $# автоматически декрементируется на 1. done
Оператор done делает возврат к началу внешнего цикла while. Если больше позициональных параметров нет, то проверка (( "$#" )) вернет false, произойдет выход из внешнего цикла, и выполнится: echo "Done." exit 0
Скрипт завершится успешно (exit-статус 0) с выводом сообщения "Done". Запуск скрипта может выглядеть следующим образом: $ ./clean-old-files.sh mydir1 mydir2
Removing mydir1/subdir1/Netscape-Navigator-1.0.zip...
Removing mydir1/subdir2/my-soon-to-be-finished-novel.rtf...
Removing mydir2/subdir1/grunge-the-new-phenomenon.doc...
Removing mydir2/subdir2/my-geocities-page.htm...
Removing mydir2/subdir3/half-life-2-leaked-beta.rar...
Done.
См. также команду getopts - парсинг аргументов, переданных в скрипт шелла [2]. [Ссылки] 1. Bash shift builtin command site:computerhope.com. |