Ein Array in Argumente eines Befehls umwandeln?

39

Ich habe eine Reihe von "Optionen" eines Befehls.

my_array=(option1 option2 option3)

Ich möchte diesen Befehl in einem Bash-Skript aufrufen und die Werte aus dem Array als Optionen verwenden. So command $(some magic here with my_array) "$1"wird:

command -option1 -option2 -option3 "$1"

Wie kann ich es tun? Ist es möglich?

Jemand verwendet Sie immer noch MS-DOS
quelle
1
Sie möchten also an -den Anfang jedes Wortes in my_arrayeinfügen?
Kevin
@ Kevin: Ja, und führen Sie es aus. Alle Zeilen befinden sich in demselben Bash-Skript. Ich frage dies, weil die gleichen Optionen an vielen Stellen im Skript verwendet werden. Anstatt sie alle zu kopieren, habe ich über ein Array nachgedacht.
Jemand benutzt Sie immer noch MS-DOS
1
Es mag sinnlos erscheinen, aber wenn Sie große Shell-Skripte erstellen, sollten Sie sich vielleicht mit Perl befassen, da ist dies sehr einfach.
Alfa64

Antworten:

43

Ich würde einen einfachen bashWeg bevorzugen :

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

Ein Grund dafür sind die Räume. Zum Beispiel, wenn Sie:

my_array=(option1 'option2 with space' option3)

Die sedbasierten Lösungen transformieren es in -option1 -option2 -with -space -option3(Länge 5), aber die obige bashErweiterung transformiert es in -option1 -option2 with space -option3(Länge noch 3). Selten, aber manchmal ist das wichtig, zum Beispiel:

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
Mann bei der Arbeit
quelle
Wenn ich das richtig verstehe, kann diese Variablensubstitution nicht in eine Funktion eingebunden oder einer Variablen zugewiesen werden, oder? Es muss direkt in den Befehlsaufruf gehen.
Konrad Rudolph
1
@KonradRudolph, können Sie es zu anderen Variablen zuweisen, solange Sie es als Array Zuordnung zu tun: somevar=("${my_array[@]/#/-}")(Beachten Sie die Klammer um den Wert.) Dann natürlich müssen Sie halten es als Array Handhabung , wenn es mit: echo "${somevar[@]}". In Bezug auf die Funktionen sind Sie dort zu eingeschränkt durch die Möglichkeiten der Sprache. Sie können ein Array übergeben: somefunc "${my_array[@]/#/-}", dann in dir es in haben $@: function somefunc() { echo "$@"; }. Dies $@enthält jedoch auch die anderen Parameter, sofern vorhanden, und keine andere Möglichkeit, das geänderte Array als stdout zurückzugeben.
Manatwork
Seltsamerweise funktioniert es nicht mit zsh. Das erste Mal bin ich auf etwas gestoßen, das in bash funktioniert, aber nicht in zsh.
Hi-Angel
@ Hi-Angel, bist du sicher, dass du @als Array-Index benutzt hast ? Ich habe es mit zsh 5.5.1 getestet und den einzigen Unterschied festgestellt, dass zsh jedem Element nur ein Präfix voranstellt, wenn es als geschrieben wird ${my_array[@]/#/-}, während bash es auch für den *Index ausführt .
Handarbeit
Oh, tut mir leid, ich hätte vielleicht etwas vermasselt, das funktioniert tatsächlich für mich!
Hi-Angel
2

Ich habe nicht verarbeitet, dass es sich um ein Array handelt, und ich habe an durch Leerzeichen getrennte Zeichenfolgen gedacht. Diese Lösung wird damit funktionieren, aber da es sich um ein Array handelt, sollten Sie die Lösung von manatwork ( @{my_array[@]/#/-}) verwenden.


Das ist nicht schlecht mit sedund eine Unterschale. Wie einfach der reguläre Ausdruck ist, hängt davon ab, was Sie für die Optionen garantieren können. Wenn alle Optionen ( a-zA-Z0-9nur) ein "Wort" sind , reicht eine einfache Anfangswortgrenze ( \<) aus:

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

Wenn Ihre Optionen (höchstwahrscheinlich -) andere Zeichen enthalten , benötigen Sie etwas Komplexeres:

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

^Stimmt mit dem Zeilenanfang [ \t]überein , stimmt mit einem Leerzeichen oder Tab \|überein , stimmt mit einer Seite ( ^oder [ \t]) überein , \( \)gruppiert (für die \|) und speichert das Ergebnis, \<stimmt mit dem Wortanfang überein. \1Beginnt die Ersetzung damit, dass die erste Übereinstimmung von den parens ( \(\)) beibehalten wird , und -fügt natürlich den Strich hinzu, den wir benötigen.

Diese arbeiten mit gnu sed zusammen, wenn sie nicht mit deiner zusammenarbeiten, lass es mich wissen.

Und wenn Sie dasselbe Produkt mehrmals verwenden, möchten Sie es möglicherweise nur einmal berechnen und speichern:

opts="$(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g')"
...
command $opts "$1"
command $opts "$2"
Kevin
quelle
Dies funktionierte nicht mit Mac OS X sed, also musste ich verwenden gsed(ich installierte mit homebrew). Eine andere Sache: Stattdessen echo $my_arraymusste ich tun echo ${my_array[@]}, um alle Gegenstände zu bekommen my_array: echo $my_arraygab mir nur das erste Element. Aber danke für die Antwort und die Erklärung des Regex, speziell zum Thema "Wortgrenze", das ist äußerst nützlich. :)
Jemand benutzt dich immer noch MS-DOS
Ich würde das Skript gerne verlassen, wenn das sed des Systems nicht mit dieser Wortgrenzenfunktion kompatibel ist. Wie würde ich das machen? Sed-Version drucken und vergleichen? Ab welcher Version von sed wurde dieses Feature hinzugefügt?
Jemand benutzt Sie immer noch MS-DOS
1
@ SomebodystillusesyouMS-DOS Mein erster Gedanke ist, direkt zu prüfen, ob es funktioniert - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
Kevin
2
Dies funktioniert nicht mehr, wenn eines der Array-Elemente Sonderzeichen enthält, am offensichtlichsten und unaufhaltsamsten Leerzeichen.
Gilles 'SO- hör auf böse zu sein'
1
@ SomebodystillusesyouMS-DOS Gilles hat recht, Sie sollten Ihr Accept in Manatwork ändern.
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'

quelle