Обробити всі аргументи, крім першого (у баш-скрипті)


444

У мене є простий скрипт, де перший аргумент зарезервований для імені файлу, а всі інші необов'язкові аргументи повинні бути передані іншим частинам сценарію.

За допомогою Google я знайшов цю вікі , але вона наводила буквальний приклад:

echo "${@: -1}"

Я нічого не можу змусити працювати, як-от:

echo "${@:2}"

або

echo "${@:2,1}"

Я отримую "погану заміну" від терміналу.

У чому проблема, і як я можу обробити всі, крім першого аргументу, переданого bash-скрипту?


21
Для того, щоб закликати когось іншого, хто заплутався, помилковий шебанг був наданий, що "{@:2}"не працює, тому правильна відповідь відповідає вище.
Гуванте

3
Ви тільки що використовували оболонку за замовчуванням, яка є тире на Ubuntu та багатьох інших Linuxes. У тирі "$ {@: -1}" інтерпретується як: {параметр: -word} - Використовуйте значення за замовчуванням та використовуйте слово, якщо параметр не визначений або недійсний. Тож у тире "$ {@: -1}" результати точно такі ж, як "$ @". Для використання bash просто використовуйте наступний перший рядок у файлі сценарію: #! / Bin / bash
лютого

Відповіді:


660

Використовуй це:

echo "${@:2}"

Такий синтаксис:

echo "${*:2}"

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


7
Я просто зрозумів, що мій шебанг поганий: #!/usr/bin/env shтому у мене виникли проблеми. Ви, наприклад, добре працює, як було зазначено вище, після того, як я зняв цей шебанг
тета

110
Використовувати "${@:2}"натомість - використовуючи *запускає всі аргументи разом як єдиний аргумент з пробілами, при цьому @зберігає перерви між ними (навіть якщо деякі аргументи містять пробіли). Різниця не помітна echo, але це важливо для багатьох інших речей.
Гордон Девіссон

2
@GordonDavisson Вся справа тут у тому, щоб запускати аргументи разом. Якби ми передавали імена файлів, ти би прав. echoдосить прощає, щоб об'єднати їх за вас; інші команди можуть бути не такими приємними. Не використовуйте лише те чи інше: дізнайтеся різницю між *і @та коли потрібно використовувати. Ви повинні використовувати їх приблизно однаково. Хороший приклад, коли це буде проблемою: якщо $3містить перерив рядка ( \n), він буде замінений пробілом, якщо у вас є $IFSзмінна за замовчуванням .
Зенексер

1
@ Zenexer: Наскільки я розумію, питання полягало в тому, як передати все, крім першого аргументу, "до іншої частини скрипту", echoтільки що використано як приклад - у такому випадку їх не слід запускати разом. На мій досвід, ситуації, коли ви хочете, щоб вони працювали разом, рідкісні (див. Це питання на одному прикладі ), і "$@"це майже завжди те, що ви хочете. Також проблема, яку ви згадуєте з розривом рядка, виникає лише в тому випадку, якщо $@(або $*) немає у подвійних лапках.
Гордон Девіссон

@GordonDavisson Хм ... ви праві, тепер, коли я думаю про це; розрив лінії не повинен бути проблемою. Я, мабуть, думав про щось інше. Однак я все одно не погоджуюся щодо їх спільного запуску; Мені потрібно $*часто використовувати в своїх сценаріях.
Zenexer

180

Якщо ви хочете рішення, яке також працює, /bin/shспробуйте

first_arg="$1"
shift
echo First argument: "$first_arg"
echo Remaining arguments: "$@"

shift [n]зміщує позиційні параметри n разів. A shiftвстановлює значення $1до значення $2, значення $2до значення $3і так далі, зменшуючи значення $#на одиницю.


25
+1: Ви, ймовірно, повинні заощадити $ 1 у змінній, перш ніж змістити її.
glenn jackman

3
Дивно foo=shiftне те, що я очікував.
Кіт Смайлі

Я знаю, що це старе, але спробуйтеfoo=$(shift)
raumaan kidwai

12
@raumaankidwai Це не працює з 2 причин: 1) shift(в оболонці) не має жодного результату. Це просто відкидає $1і зміщує все вниз. 2) $(...)запускає підпрограму, яка має свої локальні аргументи. Він зміщує аргументи в нижній частині, що не впливає на батьків
Бен Джексон

Дякую :) Чи є спосіб зробити те, що somecommand "$1" "${@:2}"робиться з цим методом (тобто зсув "inline")?
Анонім


0

Побіг цього, шукаючи щось інше. Незважаючи на те, що публікація виглядає досить старою, найпростіше рішення в bash проілюстровано нижче (принаймні bash 4), використовуючи set -- "${@:#}"де # - початковий номер елемента масиву, який ми хочемо зберегти вперед:

    #!/bin/bash

    someVar="${1}"
    someOtherVar="${2}"
    set -- "${@:3}"
    input=${@}

    [[ "${input[*],,}" == *"someword"* ]] && someNewVar="trigger"

    echo -e "${someVar}\n${someOtherVar}\n${someNewVar}\n\n${@}"

В основному, set -- "${@:3}"просто вискакує перші два елементи в масиві, як зсув Perl, і зберігається всі решта елементів, включаючи третій. Я підозрюю, що є спосіб вискочити і останні елементи.

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