Shell Script: tworzenie zmiennej z opcjami w środku

11

Mam polecenie rsync z następującymi parametrami:

rsync -avz --{partial,stats,delete,exclude=".*"}

Chcę umieścić te parametry w zmiennej, aby użyć jej później w skrypcie. Coś takiego:

#!/bin/bash
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}
$VAR /dir1 /dir2

Próbowałem z cudzysłowami, pojedynczymi cudzysłowami, nawiasami kwadratowymi, bez powodzenia.

Kellyson
źródło
Podobne pytanie na temat SO: stackoverflow.com/questions/13365553/…
Barmar
Wcześniej już tę drogę: Upewnij się, że końcowy wynikowy ciąg poleceń nie zawiera żadnych pustych ciągów. Jeśli tak, rsync może użyć ich jako parametrów, a ponieważ są niewidoczne, bardzo trudno jest je debugować. Miałem pusty pierwszy parametr i rsync zinterpretował go jako uwzględniający bieżący katalog w źródłach rzeczy do skopiowania.
Joe

Odpowiedzi:

12

Umieszczenie złożonego polecenia w zmiennej nigdy nie jest zalecanym podejściem. Zobacz BashFAQ / 050 - Próbuję umieścić polecenie w zmiennej, ale złożone przypadki zawsze zawodzą!

Twoje wymaganie staje się naprawdę proste, jeśli zdecydujesz się użyć funkcji zamiast zmiennej i przekazać do niej argumenty.

Coś jak

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --{partial,stats,delete,exclude=".*"} "$@"
}

i teraz przekaż mu wymagane argumenty jako

rsync_custom /dir1 /dir2

Definicja funkcji jest dość prosta, najpierw sprawdzamy liczbę argumentów wejściowych za pomocą zmiennej, $#która nie powinna wynosić zero. Zgłaszamy komunikat o błędzie informujący, że nie podano argumentów. Jeśli istnieją poprawne argumenty, "$@"reprezentują rzeczywiste argumenty dostarczone do funkcji.

Jeśli jest to funkcja można byłoby dość często korzystam z IE w skryptach wiersza polecenia / także dodać go do powłoki Startup-plików .bashrc, .bash_profilena przykład.

Lub, jak wspomniano, warto rozwinąć rozwinięcie nawiasu klamrowego do oddzielnych argumentów dla lepszej czytelności jako

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --partial --stats --delete --exclude=".*" "$@"
}
Inian
źródło
5
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

Ten próbuje uruchomić polecenie -avzz argumentami --partial, --statsetc .. i VARzestaw do rsyncw środowisku.

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

Cytowany formularz nie działa, ponieważ nawiasy klamrowe nie są rozwijane w cudzysłowach, a nie w przydziałach, ani też nie są rozwijane po rozwinięciu zmiennej.

Jeśli chcesz przechowywać argumenty wiersza poleceń w zmiennej, użyj tablicy:

args=(rsync -avz --{partial,stats,delete,exclude=".*"})

Teraz "${args[@]}"będzie rozwijać się rsync, -avz, --partial, itd. Jako odrębnych słów.

Tablice umożliwiają także dołączanie opcji do listy, w razie potrzeby warunkowo, dzięki czemu można np .:

args=(this that)
if something ; then
    args+=(another_arg)
fi
"$cmd" "${args[@]}"
ilkkachu
źródło
1

Możesz przynajmniej zapisać opcje częściowo w zmiennej:

opts=$(echo --{ignore-case,word-regexp,count,exclude='"sys*.*"'})

Testowanie jest ważne, ponieważ maskowanie może być trudne:

echo $opts
--ignore-case --word-regexp --count --exclude="sys*"

grep $opts bytes *.log 

Ponieważ istnieje wiele alternatyw, takich jak użycie historii, użycie aliasu, użycie funkcji, nie ma oczywistego przypadku użycia, o którym mogę myśleć. Rzadko zdarza się skomplikowana opcja udostępniania między różnymi programami, więc dla rozwiązania ad-hoc dla interaktywnej powłoki aliasing wydaje się lepszym sposobem:

alias cgrep='grep --ignore-case --word-regexp --count --exclude="sys*"'
cgrep bytes *.log

Twoja próbka

VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

nie może działać, ponieważ przydział jest endet przy pierwszym pustym polu. Musisz zamaskować puste miejsca:

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

dość niebezpieczna rzecz do testowania, z tą opcją - usuń, prawda? Ponieważ opcje mogą ponownie zawierać „,” i pojedyncze cudzysłowy, maskowanie może wkrótce stać się trudne. Wybrałbym alias lub polegał na historii.

Alias ​​może być przechowywany w pliku ~ / .bashrc do ciągłego używania przez wiele sesji. Funkcje mogą być również przechowywane w bashrc, ale potrzebujesz ich tylko, jeśli chcesz obsłużyć parametry, przekazane do funkcji, która będzie w nich oceniana.

nieznany użytkownik
źródło