Chuyển đổi một mảng thành đối số của một lệnh?


39

Tôi có một mảng "tùy chọn" của một lệnh.

my_array=(option1 option2 option3)

Tôi muốn gọi lệnh này trong một tập lệnh bash, sử dụng các giá trị từ mảng làm tùy chọn. Vì vậy, command $(some magic here with my_array) "$1"trở thành:

command -option1 -option2 -option3 "$1"

Tôi làm nó như thế nào? Có thể không?


1
Vì vậy, bạn muốn thêm -vào đầu của mỗi từ trong my_array?
Kevin

@Kevin: Vâng, và thực hiện nó. Tất cả các dòng nằm trong cùng một tập lệnh bash. Tôi đang hỏi điều này bởi vì các tùy chọn tương tự này sẽ được sử dụng ở nhiều nơi trong tập lệnh, vì vậy thay vì sao chép tất cả chúng xung quanh, tôi nghĩ về một mảng.
Ai đó vẫn sử dụng bạn MS-DOS

1
Điều này có vẻ vô nghĩa nhưng nếu bạn đang tạo ra các kịch bản shell lớn, có lẽ bạn nên xem xét kỹ về perl, loại điều này rất dễ thực hiện ở đó.
alfa64

Câu trả lời:


43

Tôi muốn một bashcách đơn giản :

command "${my_array[@]/#/-}" "$1"

Một lý do cho điều này là không gian. Ví dụ: nếu bạn có:

my_array=(option1 'option2 with space' option3)

Các sedgiải pháp dựa trên sẽ biến đổi nó trong -option1 -option2 -with -space -option3(chiều dài 5), nhưng phần bashmở rộng ở trên sẽ biến đổi nó thành -option1 -option2 with space -option3(chiều dài vẫn là 3). Hiếm khi, nhưng đôi khi điều này rất quan trọng, ví dụ:

bash-4.2$ my_array=('Ffoo bar' 'vOFS=fiz baz')
bash-4.2$ echo 'one foo bar two foo bar three foo bar four' | awk "${my_array[@]/#/-}" '{print$2,$3}'
 two fiz baz three

Nếu tôi hiểu chính xác, sự thay thế biến này không thể được gói vào một hàm, cũng không được gán cho một biến, phải không? Nó cần phải đi trực tiếp vào lệnh gọi.
Konrad Rudolph

1
@KonradRudolph, bạn có thể gán nó cho biến khác miễn là bạn thực hiện dưới dạng gán mảng: somevar=("${my_array[@]/#/-}")(Lưu ý dấu ngoặc quanh giá trị.) Tất nhiên sau đó bạn phải tiếp tục xử lý nó dưới dạng mảng khi sử dụng nó : echo "${somevar[@]}". Về các chức năng, bạn bị giới hạn bởi khả năng của ngôn ngữ. Bạn có thể truyền một mảng : somefunc "${my_array[@]/#/-}", sau đó bên trong bạn sẽ có nó trong $@: function somefunc() { echo "$@"; }. Nhưng tương tự cũng $@sẽ chứa các tham số khác, nếu tồn tại và không có cách nào khác để trả về mảng đã sửa đổi ngoài thiết bị xuất chuẩn.
manatwork

Đủ, không làm việc với zsh. Lần đầu tiên tôi tình cờ thấy một cái gì đó hoạt động trong bash nhưng không phải zsh.
Hi-Angel

@ Hi-Angel, bạn có chắc là bạn đã sử dụng @làm mảng con không? Tôi đã thử nghiệm với zsh 5.5.1 và sự khác biệt duy nhất tôi nhận thấy zsh chỉ có tiền tố mỗi mục khi được viết là ${my_array[@]/#/-}, trong khi bash cũng đã làm nó cho *đăng ký.
manatwork

Ồ, xin lỗi, tôi có thể đã làm hỏng cái gì đó, thực sự làm việc cho tôi!
Hi-Angel

2

Tôi đã không xử lý rằng nó nằm trong một mảng và đang nghĩ khoảng cách giữa các khoảng trắng trong một chuỗi. Giải pháp này sẽ hoạt động với điều đó, nhưng cho rằng đó là một mảng, đi cùng với giải pháp của manatwork ( @{my_array[@]/#/-}).


Điều này không quá tệ với sedvà một subshell. Regex dễ dàng như thế nào phụ thuộc vào những gì bạn có thể đảm bảo về các tùy chọn. Nếu các tùy chọn là tất cả một "từ" ( a-zA-Z0-9chỉ), thì một ranh giới từ bắt đầu đơn giản ( \<) sẽ đủ:

command $(echo $my_array | sed 's/\</-/g') "$1"

Nếu tùy chọn của bạn có các ký tự khác (rất có thể -), bạn sẽ cần một cái gì đó phức tạp hơn một chút:

command $(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g') "$1"

^khớp với đầu dòng, [ \t]khớp với khoảng trắng hoặc tab, \|khớp với một trong hai bên ( ^hoặc [ \t]), \( \)các nhóm (cho \|) và lưu trữ kết quả, \<khớp với bắt đầu của một từ. \1bắt đầu thay thế bằng cách giữ trận đấu đầu tiên từ parens ( \(\)) và -dĩ nhiên thêm dấu gạch ngang mà chúng ta cần.

Chúng hoạt động với gnu sed, nếu chúng không hoạt động với bạn, hãy cho tôi biết.

Và nếu bạn sẽ sử dụng cùng một thứ nhiều lần, bạn có thể chỉ cần tính toán một lần và lưu trữ:

opts="$(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g')"
...
command $opts "$1"
command $opts "$2"

Điều này không hoạt động với Mac OS X sed, vì vậy tôi cần sử dụng gsed(tôi đã cài đặt bằng cách sử dụng homebrew). Một điều khác: thay vì echo $my_array, tôi cần phải làm echo ${my_array[@]}để lấy tất cả các mục từ my_array: echo $my_arraychỉ cho tôi yếu tố đầu tiên. Nhưng cảm ơn câu trả lời và lời giải thích của regex, đặc biệt về "ranh giới từ", điều này cực kỳ hữu ích. :)
Ai đó vẫn sử dụng bạn MS-DOS

Tôi muốn thoát khỏi tập lệnh nếu sed của hệ thống không tương thích với tính năng ranh giới từ này. Làm thế nào tôi sẽ làm điều đó? In phiên bản sed và so sánh nó? Từ phiên bản nào của tính năng này đã được thêm vào?
Ai đó vẫn sử dụng bạn MS-DOS

1
@ SomebodystillusesyouMS-DOS Suy nghĩ đầu tiên của tôi là kiểm tra trực tiếp xem nó có hoạt động không - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
Kevin

2
Điều này sẽ phá vỡ nếu bất kỳ thành phần mảng nào chứa các ký tự đặc biệt, rõ ràng nhất và khoảng trắng không thể tách rời.
Gilles 'SO- ngừng trở nên xấu xa'

1
@ SomebodystillusesyouMS-DOS Gilles là đúng, bạn nên thay đổi sự chấp nhận của bạn thành manatwork.
Kevin

0
[srikanth@myhost ~]$ sh sample.sh 

-option1 -option2 -option3

[srikanth@myhost ~]$ cat sample.sh

#!/bin/bash

my_array=(option1 option2 option3)

echo ${my_array[@]} | sed 's/\</-/g'
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.