Son argümanın kabuk betiğine geçirilmesi


272

$1ilk argüman.
$@hepsi.

Kabuk betiğine geçirilen son argümanı nasıl bulabilirim?


Ben bash kullanıyordum, ama ne kadar taşınabilir çözüm o kadar iyi.
Thomas


10
use $ {! #} da kullanabilir
Prateek Joshi

11
Sadece bash için , Kevin Little'ın cevabı basit olanı önermektedir ${!#}. Kullanarak test edin bash -c 'echo ${!#}' arg1 arg2 arg3. İçin bash , ksh ve zsh , Dennis Williamson'ın cevabı önermektedir ${@: -1}. Ayrıca ${*: -1}kullanılabilir. Kullanarak test edin zsh -c 'echo ${*: -1}' arg1 arg2 arg3. Ancak bu çizgi , csh ve tcsh için işe yaramaz .
olibre

2
${!#}, aksine ${@: -1}, parametre genişletme ile çalışır. İle test edebilirsiniz bash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out.
Arch Stanton

Yanıtlar:


177

Bu biraz hack:

for last; do true; done
echo $last

Bu da oldukça taşınabilir (yine bash, ksh ve sh ile çalışmalı) ve güzel olabilecek argümanları değiştirmiyor.

Ne fordöngü yapacağını söylemezseniz, bağımsız değişkenler üzerinde dolaylı olarak döngü yapan ve döngü değişkenleri için kapsam bulunmadığı gerçeğini kullanır: ayarlandıkları son değeri korurlar.


10
@ MichałŠrajer, sanırım virgül değil kolon demek
istediniz

2
@ MichałŠrajer true, POSIX'in bir parçasıdır .
Rufflewind

4
Eski bir Solaris ile, eski bourne kabuğu ile (POSIX değil), "son olarak" $ @ "içinde yazmak zorundayım; doğru yapın; bitti"
mcoolive

8
@mcoolive @LaurenceGolsalves, daha taşınabilir olmanın yanı sıra for last in "$@"; do :; done, niyeti daha da netleştirir.
Adrian Günter

1
@mcoolive: 1979'dan itibaren unix v7 bourne kabuğunda bile çalışıyor. hem v7 sh hem de POSIX sh üzerinde çalışıyorsa daha taşınabilir olamazsınız :)
Dominik R

291

Bu yalnızca Bash:

echo "${@: -1}"

43
Benim gibi alanın neden gerekli olduğunu merak edenler için, insan bash bunun hakkında şunları söylemelidir:> Negatif bir ofsetin: - genişlemesi ile karıştırılmaması için en az bir boşlukla iki noktadan ayrılması gerektiğini unutmayın.
foo

1
Bunu kullanıyorum ve sadece Windows MSYS2 bash kırıyor. Tuhaf.
Steven Lu

2
Not: Bu yanıt , yalnızca üzerinde çalışanların aksine tüm Bash dizileri için ${@:$#}geçerlidir $@. İle $@yeni bir diziye kopyalarsanız arr=("$@"), ${arr[@]:$#}tanımsız olur. Bunun nedeni $@, "$@"genişletmelere dahil edilmeyen bir 0. öğeye sahip olmasıdır .
Bay Llama

@ Mr.Llama: Kaçınılması gereken başka bir yer, Bash'te $#diziler seyrek olduğundan ve $#dizideki elemanların sayısını son öğeye (veya eleman + 1) göstermeyeceği için diziler üzerinde yineleme yapmaktır . Başka bir deyişle, endeksler üzerinde bir for ((i = 0; i++; i < $#)); do something "${array[$i]}"; doneşey yapılmamalı ve for element in "${array[@]}"; do something "$element"; donetekrarlanmalı veya tekrarlanmalıdır: endekslerin for index in "${!array[@]}"; do something "$index" "${array[$index]}"; donedeğerleriyle bir şeyler yapmanız gerekiyorsa.
sonraki duyuruya kadar duraklatıldı.

80
$ set quick brown fox jumps

$ echo ${*: -1:1} # last argument
jumps

$ echo ${*: -1} # or simply
jumps

$ echo ${*: -2:1} # next to last
fox

Boşluk, varsayılan değer olarak yorumlanmaması için gereklidir .

Bunun sadece bash olduğunu unutmayın.


9
En iyi cevap, son argümanı da içerdiğinden. Teşekkürler!
e40

1
Steven, Ceza Kutusuna ne indiğini bilmiyorum, ama işini buraya çok seviyorum.
Bruno Bronosky

4
Evet. kısaca en iyisi. komuta ve son argüman dışında hepsi ${@: 1:$#-1}
Dyno Fu

1
@DynoFu bunun için teşekkür ederim, bir sonraki sorumu cevapladın. Yani bir vardiya şöyle görünebilir: echo ${@: -1} ${@: 1:$#-1}sonuncusu ilk olur ve geri kalanı aşağı kayar
Mike

73

Bash 3.0 veya üstü için en basit cevap

_last=${!#}       # *indirect reference* to the $# variable
# or
_last=$BASH_ARGV  # official built-in (but takes more typing :)

Bu kadar.

$ cat lastarg
#!/bin/bash
# echo the last arg given:
_last=${!#}
echo $_last
_last=$BASH_ARGV
echo $_last
for x; do
   echo $x
done

Çıktı:

$ lastarg 1 2 3 4 "5 6 7"
5 6 7
5 6 7
1
2
3
4
5 6 7

1
$BASH_ARGVbash işlevi içinde çalışmaz (yanlış bir şey yapmadıkça).
Büyük McLargeHuge

1
BASH_ARGV, bash çağrıldığında (veya bir işleve) mevcut konum bağımsız değişkenleri listesini değil, bağımsız değişkenlere sahiptir.
Isaac

Ayrıca BASH_ARGV, "son değer" yerine, verilen son bağımsız değişkenin değeri olacağına dikkat edin. Örneğin !: Eğer tek bir argüman sağlarsanız, shift kelimesini çağırırsınız, ${@:$#}hiçbir şey üretmez (çünkü tek argümanı değiştirdiniz!), BASH_ARGVYine de size (daha önce) son argümanı verecektir.
Steven Lu


29

Aşağıdakiler sizin için çalışacaktır. @ Argüman dizisi içindir. : anlamına gelir. $ #, argüman dizisinin uzunluğudur. Sonuç, son öğedir:

${@:$#} 

Misal:

function afunction{
    echo ${@:$#} 
}
afunction -d -o local 50
#Outputs 50

Bunun sadece bash olduğunu unutmayın.


Örnek bir işlev için olsa da, komut dosyaları da aynı şekilde çalışır. Bu cevabı seviyorum çünkü açık ve özlü.
Jonah Braun

1
Ve hileli değil. Yan etkiler veya özel qwerks değil, dilin açık özelliklerini kullanır. Bu kabul edilen cevap olmalı.
musicin3d

25

Son argümanı öncekilerden ayırmak için bunu buldum. Bazı cevaplar son argümanı alırken, diğer tüm argümanlara da ihtiyacınız varsa çok fazla yardımcı olmazlar. Bu çok daha iyi çalışır:

heads=${@:1:$#-1}
tail=${@:$#}

Bunun sadece bash olduğunu unutmayın.


3
Steven Penny'nin cevabı kullanım: biraz daha güzel ${@: -1}için son ve ${@: -2:1}için ikinci son (ve benzeri ...). Örnek: bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6baskılar 6. Bu akım AgileZebra yaklaşımına kalmak için, kullanım ${@:$#-1:1}almak için ikinci sondan . Örnek: bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6baskılar 5. (ve üçüncü son${@:$#-2:1} almak ve böylece ...)
olibre

4
AgileZebra'nın yanıtı , son argümanlar dışında hepsini almanın bir yolunu sunuyor, bu yüzden Steven'ın cevabının yerine geçtiğini söylemem. Ancak, $((...))1 çıkarmak için kullanmak için bir neden yok gibi görünüyor , sadece kullanabilirsiniz ${@:1:$# - 1}.
dkasak

Teşekkürler dkasak. Sadeliğinizi yansıtacak şekilde güncellendi.
AgileZebra

22

Bu, POSIX uyumlu tüm kabuklarda çalışır:

eval last=\${$#}

Kaynak: http://www.faqs.org/faqs/unix-faq/faq/part2/section-12.html


2
Daha eski bir özdeş cevaba eklenmiş bu yoruma bakın .
sonraki duyuruya kadar duraklatıldı.

1
Burada gördüğüm en basit taşınabilir çözüm. Bu bir güvenlik sorunu yok, @DennisWilliamson, $#keyfi bir dize (böyle sanmıyorum) ayarlamak için bir yol olmadığı sürece alıntı ampirik doğru yapılmış gibi görünüyor . eval last=\"\${$#}\"ayrıca çalışır ve doğrudur. Tırnakların neden gerekli olmadığını anlamıyorum.
Palec

1
İçin bash , zsh , tire ve ksh eval last=\"\${$#}\" gayet iyi. Ancak csh ve tcsh kullanımı için eval set last=\"\${$#}\". Bu örneğe bakın: tcsh -c 'eval set last=\"\${$#}\"; echo "$last"' arg1 arg2 arg3.
olibre

12

İşte benim çözüm:

  • oldukça taşınabilir (tüm POSIX sh, bash, ksh, zsh) çalışmalı
  • orijinal bağımsız değişkenleri kaydırmaz (bir kopyasını kaydırır).
  • kötülük kullanmaz eval
  • tüm liste boyunca yineleme yapmaz
  • harici araçlar kullanmaz

Kod:

ntharg() {
    shift $1
    printf '%s\n' "$1"
}
LAST_ARG=`ntharg $# "$@"`

1
Harika cevap - kısa, taşınabilir, güvenli. Teşekkürler!
Ján Lalinský

2
Bu harika bir fikir, ama birkaç önerimiz var: Birincisi, her iki etrafta eklenmelidir alıntı "$1"ve "$#"(bu büyük cevabı bakın unix.stackexchange.com/a/171347 ). İkincisi, echone yazık ki taşınabilir değildir (özellikle için -n), printf '%s\n' "$1"bunun yerine kullanılmalıdır.
joki

thanks @joki Birçok farklı unix sistemi ile çalıştım ve ben de güvenmezdim echo -n, ancak echo "$1"başarısız olacağı herhangi bir posix sisteminde farkında değilim . Her neyse, printfgerçekten daha tahmin edilebilir - güncellendi.
Michał Šrajer

@ MichałŠrajer, "$ 1" in "-n" veya "-" olduğu durumu göz önünde bulundurur, bu nedenle örneğin ntharg 1 -nveya ntharg 1 --çeşitli sistemlerde farklı sonuçlar verebilir. Şimdi sahip olduğunuz kod güvende!
joki

10

En eskiden daha yeni çözümlere:

Daha da eski en taşınabilir çözüm sh(boşluklar ve glob karakterlerle çalışır) (döngü yok, daha hızlı):

eval printf "'%s\n'" "\"\${$#}\""

Bash 2.01 sürümünden beri

$ set -- The quick brown fox jumps over the lazy dog

$ printf '%s\n'     "${!#}     ${@:(-1)} ${@: -1} ${@:~0} ${!#}"
dog     dog dog dog dog

Ksh, zsh ve bash için:

$ printf '%s\n' "${@: -1}    ${@:~0}"     # the space beetwen `:`
                                          # and `-1` is a must.
dog   dog

Ve "sondan diğerine" için:

$ printf '%s\n' "${@:~1:1}"
lazy

Bir tire işareti ile başlayan bağımsız değişkenlerle ilgili sorunları gidermek için printf kullanma -n ) .

Tüm mermiler ve daha yaşlılar için sh(boşluklar ve glob karakterlerle çalışır):

$ set -- The quick brown fox jumps over the lazy dog "the * last argument"

$ eval printf "'%s\n'" "\"\${$#}\""
The last * argument

Veya bir lastvar ayarlamak istiyorsanız :

$ eval last=\${$#}; printf '%s\n' "$last"
The last * argument

Ve "sondan diğerine" için:

$ eval printf "'%s\n'" "\"\${$(($#-1))}\""
dog



3
shift `expr $# - 1`
echo "$1"

Bu, bağımsız değişkenleri eksi 1 bağımsız değişken sayısına göre kaydırır ve sonuncu olan ilk (ve yalnızca) kalan bağımsız değişkeni döndürür.

Sadece bashta test ettim, ama sh ve ksh'da da çalışmalı.


11
shift $(($# - 1))- harici bir yardımcı programa gerek yoktur. Bash, ksh, zsh ve dash'de çalışır.
sonraki duyuruya kadar duraklatıldı.

@Dennis: Güzel! $((...))Sözdizimini bilmiyordum .
Laurence Gonsalves

(Örneğin için ) gibi printf '%s\n' "$1"beklenmedik davranışlardan kaçınmak için kullanmak istersiniz . echo-n
joki

2

Bunu yıkıcı olmayan bir şekilde yapmak istiyorsanız, bir yol tüm argümanları bir işleve iletmek ve sonuncuyu döndürmektir:

#!/bin/bash

last() {
        if [[ $# -ne 0 ]] ; then
            shift $(expr $# - 1)
            echo "$1"
        #else
            #do something when no arguments
        fi
}

lastvar=$(last "$@")
echo $lastvar
echo "$@"

pax> ./qq.sh 1 2 3 a b
b
1 2 3 a b

Aslında umursamıyorsan diğer argümanlar tutma konusunda, bir işlevde buna ihtiyacı yoktur ama onlar zaten işlendikten sonra sürece diğer argümanlar tutmak istiyorum asla nerede bir durumun zor zamanlar düşünme var, bu durumda bunları sırayla işleme koymak için process / shift / process / shift / ...

Ben size, çünkü bunları tutmak istiyorsanız burada tahmin ediyorum değil sıralı yöntem izledi. Bu yöntem, "" döndüren bağımsız değişken olmayan durumu da ele alır. Açıklanan elseyan tümcesini ekleyerek bu davranışı kolayca ayarlayabilirsiniz .


2

Tcsh için:

set X = `echo $* | awk -F " " '{print $NF}'`
somecommand "$X"

Bu görev dışında taşınabilir bir çözüm olacağından eminim.


Bunu sonsuza kadar önce gönderdiğini biliyorum, ama bu çözüm harika - birinin tcsh olduğunu sevindim!
user3295674

2

@ AgileZebra'nın cevabını (artı @ starfry'nin yorumu) en yararlı buldum, ancak headsbir skaler olarak ayarlandı . Bir dizi muhtemelen daha kullanışlıdır:

heads=( "${@:1:$(($# - 1))}" )
tail=${@:${#@}}

Bunun sadece bash olduğunu unutmayın.


Kafaları bir diziye nasıl dönüştürürdünüz?
Stephane

Zaten parantez ekleyerek yaptım, örneğin echo "${heads[-1]}"son öğeyi yazdırır heads. Yoksa bir şey mi kaçırıyorum?
EndlosSchleife

1

Kullanarak bir çözüm eval:

last=$(eval "echo \$$#")

echo $last

7
Dolaylı başvuru için eval kötü uygulama ve büyük bir güvenlik endişesini (bahsetmiyorum, overkill değerdir değil alıntı içinde yankı veya dışında $ () Bash herhangi for değişken dolaylı referanslar için yerleşik bir sözdizimi vardır. !Yani last="${!#}"aynı kullanırsınız . yaklaşım, çok daha güvenli, kompakt, yerleşik, aklı başında bir şekilde (dolaylı $ # referans) Ve düzgün alıntılanan.
MestreLion

Aynısını başka bir cevapta gerçekleştirmenin daha temiz bir yolunu görün .
Palec

@MestreLion, RHS tutarında gerekli değildir =.
Tom Hale

1
@TomHale: özel durumu için true ${!#}, ancak genel olarak değil: içerik gibi gerçek boşluk içeriyorsa tırnak işaretleri gereklidir last='two words'. $()İçeriğe bakılmaksızın yalnızca boşlukta güvenlidir.
MestreLion

1

Yukarıdaki cevapları okuduktan sonra çalıştırılabilir görüntü PGM üretmek için PGM.cpp üzerinde g ++ çalıştırmak için bir Q & D kabuk betiği (sh ve bash üzerinde çalışması gerekir) yazdı. Komut satırındaki son bağımsız değişkenin dosya adı (.cpp isteğe bağlıdır) ve diğer tüm bağımsız değişkenlerin seçenek olduğunu varsayar.

#!/bin/sh
if [ $# -lt 1 ]
then
    echo "Usage: `basename $0` [opt] pgm runs g++ to compile pgm[.cpp] into pgm"
    exit 2
fi
OPT=
PGM=
# PGM is the last argument, all others are considered options
for F; do OPT="$OPT $PGM"; PGM=$F; done
DIR=`dirname $PGM`
PGM=`basename $PGM .cpp`
# put -o first so it can be overridden by -o specified in OPT
set -x
g++ -o $DIR/$PGM $OPT $DIR/$PGM.cpp

1

Aşağıdakiler, LASTmevcut ortamı değiştirmeden son argüman olarak ayarlanacaktır :

LAST=$({
   shift $(($#-1))
   echo $1
})
echo $LAST

Başka argümanlara artık ihtiyaç duyulmuyorsa ve kaydırılabilirse, basitleştirilebilir:

shift $(($#-1))
echo $1

Taşınabilirlik nedeniyle aşağıdaki nedenlerle:

shift $(($#-1));

ile değiştirilebilir:

shift `expr $# - 1`

$()Backquotes ile de değiştiririz :

LAST=`{
   shift \`expr $# - 1\`
   echo $1
}`
echo $LAST

1
echo $argv[$#argv]

Şimdi sadece bazı metinler eklemem gerekiyor çünkü cevabım yayınlamak için çok kısa. Düzenlemek için daha fazla metin eklemem gerekiyor.


Bu hangi kabukta çalışacak? Bash değil. Balık değil (var $argvama değil $#argv- $argv[(count $argv)]balıklarda çalışır).
Beni Cherniavsky-Paskin

1

Bu benim kopyalama fonksiyonumun bir parçası:

eval echo $(echo '$'"$#")

Komut dosyalarında kullanmak için şunu yapın:

a=$(eval echo $(echo '$'"$#"))

Açıklama (en içteki ilk):

  1. $(echo '$'"$#")döner $[nr]nerede [nr]parametrelerin sayısıdır. Ör. Dize$123 (genişletilmemiş).
  2. echo $123 değerlendirildiğinde 123 parametresinin değerini döndürür.
  3. evalsadece $123parametrenin değerine genişler , örn last_arg. Bu bir dize olarak yorumlanır ve döndürülür.

Bash ile 2015 ortasından itibaren çalışıyor.


Değerlendirme yaklaşımı burada zaten birçok kez sunulmuştur, ancak bunun nasıl çalıştığına dair bir açıklaması vardır. Daha da geliştirilebilir, ancak hala tutmaya değer.
Palec

0
#! /bin/sh

next=$1
while [ -n "${next}" ] ; do
  last=$next
  shift
  next=$1
done

echo $last

Bir argüman boş dize ise bu başarısız olur, ancak vakaların% 99.9'unda çalışır.
Thomas

0

Son argümanı bulmak için aşağıdaki komut dosyasını deneyin

 # cat arguments.sh
 #!/bin/bash
 if [ $# -eq 0 ]
 then
 echo "No Arguments supplied"
 else
 echo $* > .ags
 sed -e 's/ /\n/g' .ags | tac | head -n1 > .ga
 echo "Last Argument is: `cat .ga`"
 fi

Çıktı:

 # ./arguments.sh
 No Arguments supplied

 # ./arguments.sh testing for the last argument value
 Last Argument is: value

Teşekkürler.


Bunun ./arguments.sh "son değer" ile başarısız olacağını sanıyorum
Thomas

Thomas kontrol ettiğiniz için teşekkür ederiz, ben # ./arguments.sh "son değer" mentinoed gibi komut dosyası gerçekleştirmek için çalıştım Son Tartışma: değer şimdi iyi çalışıyor. # ./arguments.sh "double ile son değer kontrolü" Last Argument: double
Ranjithkumar T

Sorun şu ki, son argüman değer değil, 'son değer' idi. Hata, boşluk içeren argümandan kaynaklanır.
Thomas

0

Bunu yapmanın çok daha özlü bir yolu var. Bir bash betiğinin bağımsız değişkenleri, bir dizi haline getirilebilir, bu da öğelerle başa çıkmayı çok daha basit hale getirir. Aşağıdaki komut dosyası her zaman bir komut dosyasına geçirilen son argümanı yazdırır.

  argArray=( "$@" )                        # Add all script arguments to argArray
  arrayLength=${#argArray[@]}              # Get the length of the array
  lastArg=$((arrayLength - 1))             # Arrays are zero based, so last arg is -1
  echo ${argArray[$lastArg]}

Örnek çıktı

$ ./lastarg.sh 1 2 buckle my shoe
shoe

0

Parametre genişletmeyi kullanma (eşleşen başlangıcı sil):

args="$@"
last=${args##* }

Sondan önce hepsini almak da kolaydır:

prelast=${args% *}


Bu yalnızca son bağımsız değişken boşluk içermiyorsa çalışır.
Palec

Son argüman çift tırnak içine alınmış bir cümle ise, söz konusu cümlenin bir argüman olması gerekiyorsa, önyargınız başarısız olur.
Stephane

0

En son kullanılan komutun son bağımsız değişkenini döndürmek için özel parametreyi kullanın:

$_

Bu durumda, başka bir komut çağrılmadan önce kod içinde kullanılırsa çalışır.



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.