manipolare i parametri in sh
Domanda
Sto lavorando con un'utilità (Unison, ma non è questo il punto) che accetta parametri come:
$ unison -path path1 -path path2 -path path3
Vorrei scrivere una sceneggiatura che potrei eseguire così:
$ myscript path1 path2 path3
Spero in una soluzione conforme a Posix, ma anche Bash-specifico sarebbe buono.
Immagino che dovrebbe essere qualcosa di simile:
#!/bin/sh
unison ${*/ / -path }
Ma questo non funziona.
MODIFICARE: Ok, penso di avere qualcosa:
#!/bin/bash
PARAMS=
for arg in "$@"
do
PARAMS+=" -path '$arg'"
done
unison $PARAMS
I problemi sono questo che funziona solo in bash e sono abbastanza sicuro che ci sia un modo migliore per citare i parametri.
Soluzione
Incontrollato, potrebbe essere semplice come:
exec unison -path $1 -path $2 -path $3
Se non incorpori spazi nei nomi del tuo percorso, puoi gestire un numero variabile di argomenti con:
arglist=""
for path in "$@"
do
arglist="$arglist -path $path"
done
exec unison $arglist
Se hai spazi nei nomi del tuo percorso, allora devi lavorare molto più duramente; Di solito uso un programma personalizzato chiamato escape
, che cita argomenti che devono essere citati e eval
:
arglist=""
for path in "$@"
do
path=$(escape "$path")
arglist="$arglist -path $path"
done
eval exec unison "$arglist"
Noto che l'uso di Perl o Python renderebbe più facile la gestione di argomenti con spazi, ma la domanda pone su Shell.
Potrebbe anche essere fattibile in bash usare una variabile di array di shell: costruire gli argomenti in un array e passare l'array come argomenti al unison
comando.
Altri suggerimenti
Se usi gli array di Bash, tutti i tuoi problemi di citazione scompaiono.
#!/bin/bash
args=()
for i in "$@"; do
# With Bash >= 3:
args+=(-path "$i")
# +=() doesn't work in Bash 2
# args=("${args[@]}" -path "$i")
done
exec unison "${args[@]}"
In Bash, puoi usare "${@/#/-path }"
che sostituirà l'inizio di ciascun parametro posizionale con "-path". Per rappresentare la fine dell'uso della stringa %
invece di #
.
Ecco uno script demo semplice usando sed
e ripetuto -e
opzioni. (Naturalmente ci sono modi più efficienti per utilizzare sed
.)
#!/bin/bash
echo "Resulting arguments: ${@/#/-e }"
sed "${@/#/-e }"
E eseguendolo in questo modo:
$ echo abc | demo s/a/A/ s/b/B/
Noi abbiamo:
Resulting arguments: -e s/a/A/ -e s/b/B/
ABc
Se vuoi una versione pesantemente specifica per bash, puoi provare
#! /bin/sh eval eval exec \ unison -path\\ \\\"{$(eval echo \\\"\\\${1..$#}\\\" | sed 's/ /,/g')}\\\"
Se rimuovi tutti i personaggi di tripla backslash, questo diventa più facile da capire, ma non rovinerò il divertimento spiegandolo :-)
La complicazione principale è gestire i nomi dei file con spazi. Ciò spiega la citazione tripla-backlash e il doppio eval.
Ecco come puoi citare in modo soddisfacente i tuoi personaggi:
job_strategy()
{
local p
for p in "$@"; do
printf '-path\000%s\000' "$p"
done
}
job_process()
{
xargs -0 unison
}
job_strategy "path1" "path2" "path3" | job_process