$ @, крім першого аргументу


36

Мені потрібно написати скрипт оболонки, який працює таким чином:

./myscript arg1 arg2_1 arg2_2 arg2_3 ....... arg2_#

всередині скрипту є цикл for

for i in $@

Однак, як я знаю, $ @ включає $ 1 до $ ($ # - 1). Але для моєї програми $ 1 суттєво відрізняється від $ 2 $ 3 $ 4 і т. Д. Я хотів би перетворити цикл від $ 2 до кінця ... Як я цього досягти? Дякую:)

Відповіді:


47

По-перше, зауважте, що $@без лапок немає сенсу і не слід їх використовувати. $@слід використовувати лише цитуючи ( "$@") та в контексті списку.

for i in "$@" кваліфікується як контекст списку, але тут, для переходу на позиційні параметри, канонічною, найбільш портативною та простішою формою є:

for i
do something with "$i"
done

Тепер, щоб провести цикл на елементи, починаючи з другого, використовується канонічний і найбільш портативний спосіб shift:

first_arg=$1
shift # short for shift 1
for i
do something with "$i"
done

Після цього shiftте, що раніше $1було видалено зі списку (але ми зберегли його в $first_arg), і те, що раніше було $2в ньому $1. Позиційні параметри зміщені 1 вліво (використовуйте shift 2для зміщення на 2 ...). Отже, наша петля переходить від того, що було другим аргументом до останнього.

З bashzshта ksh93, але це так), альтернатива зробити:

for i in "${@:2}"
do something with "$i"
done

Але зауважте, що це не стандартний shсинтаксис, тому його не слід використовувати в сценарії, який починається з #! /bin/sh -.

У zshабо yashви також можете зробити:

for i in "${@[3,-3]}"
do something with "$i"
done

перевести цикл від 3-го до 3-го останнього аргументу.

В zsh, $@також відомий як $argvмасив. Отже, щоб виводити елементи з початку чи кінця масивів, ви також можете зробити:

argv[1,3]=() # remove the first 3 elements
argv[-3,-1]=()

( shiftТакож може бути записана 1=()в zsh)

У програмі bashви можете призначити лише $@елементи за допомогою setвбудованого, щоб вивести 3 елементи з кінця, це було б щось на зразок:

set -- "${@:1:$#-3}"

І перевести цикл від 3-го до 3-го останнього:

for i in "${@:3:$#-5}"
do something with "$i"
done

POSIXly, щоб вивести останні 3 елементи "$@", вам потрібно використовувати цикл:

n=$(($# - 3))
for arg do
  [ "$n" -gt 0 ] && set -- "$@" "$arg"
  shift
  n=$((n - 1))
done

2
Альтернативна (і потворна) можливість for ((i=2; i<=$#; i++)); do something with "${!i}"; done
удару

Я більше знайомий з цією версією, так як я більше знайомий з ++ :)
user40780

10

Я думаю, ти хочеш shiftвбудованого. Він перейменовується $2на $1, $3до $2тощо.

Подобається це:

shift
for i in "$@"; do
    echo $i
done

Ви могли б пояснити більш детально, як я досягти цього в циклі for? Дякую.
user40780

1
Ви цього не зробите - ви використовуєте його до входу в forцикл, тоді ви просто перебираєте $ @ як правило. Після shiftдзвінка $ @ має бутиarg2_1 arg2_2 arg2_3...
Джон

Однак у мене виникне ще одне запитання: припустимо, я хочу перевести цикл від $ 1 до $ ($ # - 2) (тобто arg_1 до arg_2 _ # - 1, крім arg_2 _ #) ... Що мені робити?
user40780

2

Завжди існує підхід печера:

first=1
for i
do
        if [ "$first" ]
        then
                first=
                continue
        fi
        something with "$i"
done

Це залишає $@недоторканим (у випадку, якщо ви хочете використовувати його пізніше) і просто перебирає цикл на кожен аргумент, але не обробляє перший.


1

У bash ви також можете написати цей цикл із явним індексуванням:

for ((i=2; i<=$#; ++i)); do
  process "${!i}"
done

Це повторює всі аргументи від другого до останнього. Якщо ви хочете замість цього виключити останній аргумент, просто зробіть це

for ((i=1; i<=$#-1; ++i)); do
  process "${!i}"
done

і якщо ви хочете брати будь-який інший аргумент, запишіть його як

for ((i=1; i<=$#; i+=2)); do
  process "${!i}"
done

Історія, що стоїть за цим, - це арифметична версія forвбудованого , поєднана з підрахунком аргументів$# та змінною непрямістю${…} .

Одне приємне додаток полягає в тому, що ви можете використовувати це для того, щоб вирішити, що знаходиться в циклі, чи буде даний параметр приймати аргумент, який слід за ним, як значення. Якщо це так, приріст i(наприклад, запис : $((++i))) споживає наступне значення і пропускає його під час ітерації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.