$ @ ngoại trừ đối số 1


36

Tôi cần phải viết một kịch bản shell chạy theo cách này:

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

có một vòng lặp for bên trong script

for i in $@

Tuy nhiên, như tôi biết, $ @ bao gồm $ 1 đến $ ($ # - 1). Nhưng đối với chương trình của tôi, $ 1 khác biệt rõ rệt với $ 2 $ 3 $ 4, v.v. Tôi muốn lặp từ $ 2 đến hết ... Làm thế nào để tôi đạt được điều này? Cảm ơn bạn:)

Câu trả lời:


47

Đầu tiên, lưu ý rằng $@không có trích dẫn sẽ không có ý nghĩa và không nên được sử dụng. $@chỉ nên được sử dụng trích dẫn ( "$@") và trong bối cảnh danh sách.

for i in "$@" đủ điều kiện làm bối cảnh danh sách, nhưng ở đây, để lặp lại các tham số vị trí, dạng chính tắc, di động nhất và đơn giản hơn là:

for i
do something with "$i"
done

Bây giờ, để lặp lại các phần tử bắt đầu từ phần thứ hai, cách thức hợp quy và di động nhất là sử dụng shift:

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

Sau đó shift, những gì $1đã từng bị xóa khỏi danh sách (nhưng chúng tôi đã lưu nó vào $first_arg) và những gì trước $2đây đã có trong đó $1. Các tham số vị trí đã được dịch chuyển 1 vị trí sang trái (sử dụng shift 2để dịch chuyển 2 ...). Về cơ bản, vòng lặp của chúng ta đang lặp từ những gì từng là đối số thứ hai đến cuối cùng.

Với bash(và zshksh93, nhưng đó của nó), một sự thay thế là để làm:

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

Nhưng lưu ý rằng đó không phải là shcú pháp chuẩn nên không được sử dụng trong tập lệnh bắt đầu bằng #! /bin/sh -.

Trong zshhoặc yash, bạn cũng có thể làm:

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

để lặp từ đối số thứ 3 đến thứ 3 cuối cùng.

Trong zsh, $@còn được gọi là $argvmảng. Vì vậy, để các phần tử pop từ đầu hoặc cuối của mảng, bạn cũng có thể làm:

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

( shiftCũng có thể được viết 1=()trong zsh)

Trong bash, bạn chỉ có thể gán cho các $@phần tử có setnội dung, vì vậy để bật 3 phần tử ở cuối, đó sẽ là một cái gì đó như:

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

Và để lặp từ thứ 3 đến thứ 3 cuối cùng:

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

POSIXly, để bật 3 yếu tố cuối cùng "$@", bạn cần sử dụng một vòng lặp:

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

2
Một khả năng bash thay thế (và xấu xí): các biến gián tiếp:for ((i=2; i<=$#; i++)); do something with "${!i}"; done
glenn jackman

Tôi quen thuộc hơn với phiên bản này, vì tôi quen thuộc hơn với c ++ :)
user40780

10

Tôi nghĩ rằng bạn muốn shiftdựng sẵn. Nó đổi tên $2thành $1, $3thành $2, v.v.

Như thế này:

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

bạn có thể giải thích chi tiết hơn làm thế nào để tôi đạt được điều đó trong vòng lặp for? Cảm ơn bạn.
dùng40780

1
Bạn không - bạn sử dụng nó trước khi vào forvòng lặp, sau đó bạn chỉ cần lặp qua $ @ bình thường. Sau shiftcuộc gọi, $ @ nên làarg2_1 arg2_2 arg2_3...
John

Tuy nhiên, tôi sẽ có thêm một câu hỏi: Giả sử tôi muốn lặp từ $ 1 cho đến $ ($ # - 2) (tức là arg_1 cho đến arg_2 _ # - 1, ngoại trừ arg_2 _ #) ... Tôi nên làm gì?
dùng40780

2

Luôn luôn có cách tiếp cận thượng cổ:

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

Điều này $@vẫn còn nguyên (trong trường hợp bạn muốn sử dụng nó sau này) và chỉ đơn giản là lặp lại mọi đối số, nhưng không xử lý đối số đầu tiên.


1

Trong bash, bạn cũng có thể viết vòng lặp đó với lập chỉ mục rõ ràng:

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

Điều này lặp đi lặp lại trên tất cả các đối số từ cái thứ hai đến cái cuối cùng. Nếu bạn muốn loại trừ đối số cuối cùng thay vào đó, chỉ cần thực hiện điều này

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

và nếu bạn chỉ muốn nhận mọi đối số khác, hãy viết nó thành

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

Câu chuyện đằng sau điều này là phiên bản số học của fornội dung , kết hợp với số lượng đối số$# và chỉ định biến${…} .

Một ứng dụng hay là bạn có thể sử dụng điều này để quyết định, bên trong vòng lặp, liệu một tùy chọn đã cho sẽ lấy đối số theo sau nó làm giá trị hay không. Nếu có, tăng i(ví dụ bằng văn bản : $((++i))) để sử dụng giá trị tiếp theo và bỏ qua nó trong quá trình lặp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.