Kabuk bağımsız değişkenlerini ters sırada yazdırma


12

Biraz sıkıştım. Benim görevim argümanları üçüncü ve dördüncü hariç ters sırayla yazdırmak.

Ne var bu kod:

#!/bin/bash

i=$#
for arg in "$@"
do
    case $i
    in
        3) ;;
        4) ;;
        *) eval echo "$i. Parameter: \$$i";;
    esac
    i=`expr $i - 1`
done

Eval (PHP selamlar) nefret gibi ben onsuz bir çözüm arıyorum ama bir tane bulamıyorum.

Argümanın konumunu dinamik olarak nasıl tanımlayabilirim?

PS: Hayır bu bir ev ödevi değil, bir sınav için kabuk öğreniyorum, bu yüzden eski sınavları çözmeye çalışıyorum.

Yanıtlar:


13

evaldinamik olarak seçilen konumu ile konumsal bir parametreye erişmenin tek taşınabilir yoludur. Değerler (kullanmadığınız) yerine dizinde açıkça döngü oluşturursanız, komut dosyanız daha temiz olur. exprSenaryonuzun antika Bourne mermilerinde çalışmasını istemediğiniz sürece ihtiyacınız olmadığını unutmayın; $((…))aritmetik POSIX içinde. Kullanımını evalmümkün olan en küçük parça ile sınırlayın ; örneğin, kullanmayın eval echo, değeri geçici bir değişkene atayın.

i=$#
while [ "$i" -gt 0 ]; do
  if [ "$i" -ne 3 ] && [ "$i" -ne 2 ]; then
    eval "value=\${$i}"
    echo "Parameter $i is $value"
  fi
  i=$((i-1))
done

Bash'de, ${!i}adı olan parametrenin değerini ifade etmek için kullanabilirsiniz $i. Bu $i, adlandırılmış bir parametre veya bir sayı (konumsal bir parametreyi gösterir) olduğunda çalışır. Siz oradayken, diğer bash kolaylık özelliklerinden yararlanabilirsiniz.

for ((i=$#; i>0; i--)); do
  if ((i != 3 && i != 4)); then
    echo "Parameter $i is ${!i}"
  fi
done

argOnlar doğru sipariş ve ters değil gibi kullanamazsınız . Kullanımıyla ilgili olarak expr, sadece standardı kullanmakla sınırlıyım.
WarrenFaith

1
@WarrenFaith Komut dosyanızla başlarsa #!/bin/bash, ${!i}ve öğelerini kullanabilirsiniz (()). Standart sh'ye bağlı kalmak istiyorsanız , bunlar mevcut değildir, ancak $((…))vardır.
Gilles 'SO- kötü olmayı bırak'

Tamam, sanırım bununla çalışabilirim :)
WarrenFaith

Antika Bourne mermilerinin zaten 9 doların üzerindeki konum parametrelerine erişemeyeceğini unutmayın.
Stéphane Chazelas

1
evalolduğu değil sadece taşınabilir yolu Ryan gösterdiği gibi .
Stéphane Chazelas

7

Bunu reverseyapan benim yolumda bir komut dosyası tutmak :

#!/bin/sh

if [ "$#" -gt 0 ]; then
    arg=$1
    shift
    reverse "$@"
    printf '%s\n' "$arg"
fi

Örnek kullanım:

$ reverse a b c '*' '-n'
-n
*
c
b
a

Ayrılmış komut dosyası yerine bir işlev de kullanabilirsiniz.


Birinin (şu ana kadar gönderilen diğer cevapların aksine) Bourne kabuğunda da işe yarayacağını unutmayın (günümüzde bu kabuğun kullanılması için herhangi bir neden yoktur)
Stéphane Chazelas

@ StéphaneChazelas: Neden alıntı yapmalı $#? Negatif olmayan bir tam sayıdan başka bir şey olabilir mi?
G-Man, 'Monica'yı Yeniden Başlat' diyor

2
@ G-Man, liste bağlamında sıralanmamış bir değişkeni bırakmak split + glob operatörünü çağırıyor. Onu burada çağırmak istemenizin bir nedeni yok. Bunun değişkenin içeriği ile ilgisi yoktur. Ayrıca sonuna kadar unix.stackexchange.com/a/171347 adresine bakın . Ayrıca bazı mermilerin (örneğin, şık, hala) IFS'yi yine de ortamdan devraldığını unutmayın.
Stéphane Chazelas

Android'de beyan etmek zorunda olduğumu buldumlocal arg=$1
starfry


2

Konumsal parametrelerin yeni satır karakterleri içermediği varsayılarak:

[ "$#" -gt 0 ] && printf '%s\n' "$@" | #print in order
sed '3,4 d' |                          #skip 3 and 4
tac                                    #reverse (try tail -r on
                                       #non-GNU systems).

Ölçek:

set 1 2 3 4 5 6
printf '%s\n' "$@" | 
sed '3,4 d' |
tac

Test çıktısı:

6
5
2
1

1

İle zsh:

$ set a 'foo bar' c '' '*'
$ printf '<%s>\n' "${(Oa)@}"
<*>
<>
<c>
<foo bar>
<a>

Oadizi öğelerini, ters dizi dizinlerinde genişletme üzerine sıralamak için bir parametre genişletme bayrağdır.

3 ve 4'ü hariç tutmak için:

$ printf '<%s>\n' "${(Oa)@[5,-1]}" "${(Oa)@[1,2]}"
<*>
<foo bar>
<a>

0

Temel olarak herhangi bir kabukla:

printf '{ PS4=\${$(($#-$x))}; } 2>&3; 2>&1\n%.0s' |
x=LINENO+1 sh -sx "$@" 3>/dev/null

Ve alt kabuk kullanmanıza gerek yok . Örneğin:

set -x a b c
{ last= PS4=\${last:=\${$#}}; set +x; } 2>/dev/null
echo "$last"

... baskılar ...

c

Ve işte aliassizin için argümanları ileri veya geri yazdıracak bir kabuk ayarlayabilen bir kabuk işlevi :

tofro() case $1 in (*[!0-9]*|'') ! :;;(*) set "$1"
        until   [ "$1" -eq "$(($#-1))" ]    &&
                shift && alias args=":; printf \
            \"%.\$((\$??\${#*}:0))s%.\$((!\$??\${#*}:0))s\n\" $* "
        do      [ "$#" -gt 1 ] &&
                set "$@ \"\${$#}\" " '"${'"$((1+$1-$#))"'}"' ||
                set "$1" '"$1" "${'"$1"'}"'
        done;   esac

Herhangi bir argüman için değişmez değerleri depolamaya çalışmaz, bunun yerine şuna böyle bir dize koyar args alias:

:;printf    "%.$(($??${#*}:0))s%.$((!$??${#*}:0))s\n" \
            "$1" "${3}" "${2}"  "${2}" "${3}"  "${1}"

... ve böylece sadece ileri ve geri parametrelerdeki referansları saklar. Bağımsız değişken olarak verildiği şekilde bir sayıyı depolar. Ve böylece yukarıdaki aliasgibi üretildi:

tofro 3

printf'nin davranışı, önceki komutun dönüş değerine göre etkilenir - bu her zaman :null komuttur ve bu nedenle genellikle doğrudur. printfher yazdırdığında argümanlarının yarısını atlar - varsayılan olarak, en küçük numaralıdan en büyüğe yazdırılan bağımsız değişkenleri alır. Ancak, yalnızca şunları yaparsanız:

! args

... tam tersini yazdırıyor.

Takma ad hiçbir değişmez değeri depolamadığından, gerçek bağımsız değişkenler değişebilirken değeri sabit kalır, ancak yine de olabildiğince çok başvurur. Örneğin:

set one two three
tofro 3
args; ! args
shift; args; ! args

... hangi baskı ...

one
two
three
three
two
one
two
three


three
two

Ancak takma adı sıfırlamak şu şekilde yapılabilir:

tofro 2
args; ! args

... ve böylece yazdırılıyor ...

two
three
three
two

-3
declare -a argv=( "$@" )
for (( i=$((${#argv[@]}-1)) ; i>=0 ; i=$(($i-1)) )) ; do
        echo "${argv[$i]}"
        # use "${argv[$i]}" in here...
done

2
hiçbir açıklama, güzel. Benden alçalt. Kodunuzun ne yaptığını açıklayın ve fikrimi değiştirebilirim.
WarrenFaith

3
Basitleştirilmişfor ((i = $# - 1; i >= 0; i--))
Stéphane Chazelas

Halüsinasyon görüyor muyum? "Üçüncüsü ve dördüncü hariç ... argümanları yazdır" yazan soruda kelimeler gördüğümü sanıyordum.
G-Man, 'Monica'yı Yeniden Başlat' diyor

1
@ G-Man, başlık "eval kullanmadan cevap" ters argüman yazdır "diyor. 3 ve 4'ü bunun dışında tutmak önemsizdir. Downvotes'un haklı olduğunu düşünmüyorum. Aynı zamanda kendi kendini açıklayıcıdır (dediğim gibi çok basitleştirilebilir).
Stéphane Chazelas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.