Shell komut dosyalarında shift kullanmanın amacı nedir?


118

Bu senaryoya rastladım:

#! /bin/bash                                                                                                                                                                                           

if (( $# < 3 )); then
  echo "$0 old_string new_string file [file...]"
  exit 0
else
  ostr="$1"; shift
  nstr="$1"; shift  
fi

echo "Replacing \"$ostr\" with \"$nstr\""
for file in $@; do
  if [ -f $file ]; then
    echo "Working with: $file"
    eval "sed 's/"$ostr"/"$nstr"/g' $file" > $file.tmp 
    mv $file.tmp $file
  fi  
done

Kullandıkları çizgilerin anlamı nedir shift? Senaryo en azından argümanlarla kullanılmalı, yani ...?

Yanıtlar:


141

shiftbashargüman listesinin başındaki argümanları kaldıran bir yerleşiktir. Komut verilen 3 argümanlar mevcut olduğunu göz önüne alındığında $1, $2, $3, daha sonra bir çağrı için shift yapacak $2yeni $1. Bir shift 2ikiye kaymak $1, eskiyi yeniler $3. Daha fazla bilgi için buraya bakınız:


25
Bu değil + 1 Not döner bunları, bu kadar değişen (bağımsız değişkenler) dizisini onları. Shift / shift ve pop / push, bu genel manipülasyon için ortak adlardır (bkz. Örneğin bash pushdve popd).
goldilocks


@goldilocks çok doğru. "Döndür" yerine, başka bir sözcüğü kullanmanın daha iyi bir yolunu bulmalıydım, "argüman değişkenlerinde argümanları ileri götür" gibi bir şey. Neyse, metindeki örneklerin ve referansın bağlantısının yine de açıklığa kavuştuğunu umuyorum.
insanlıkANDpeace

Senaryodan bahsetmesine rağmen, bashsoru tüm kabuklara genel, bu nedenle Posix standardı hüküm sürüyor
calandoa

32

Goldilocks' yorum ve insanlığın referanslar tarifine göre shift(konumsal parametreler yeniden atar $1, $2böylece, vs.) $1eski değerini alır $2, $2değeri alır $3, vb *   eski değerini $1atılır. ( $0değişmez.) Bunu yapmanın bazı nedenleri şunlardır:

  • Onuncu argümana (varsa) daha kolay erişmenizi sağlar.  $10çalışmaz - $1bir ile birleşmiş olarak yorumlanır 0 (ve bu yüzden benzer bir şey üretebilir Hello0). Bir sonra shift, onuncu argüman olur $9. (Ancak, çoğu modern mermide kullanabilirsiniz ${10}.)
  • As Başlayanlar için Bash Kılavuzu gösteriyor, bu argümanları döngü kullanılabilir. IMNSHO, bu sakar; forbunun için çok daha iyi.
  • Örnek komut dosyanızda olduğu gibi, birkaç değişken dışında tüm argümanları aynı şekilde işlemeyi kolaylaştırır. Örneğin, komut dosyanızda $1ve $2metin dizeleri, süre $3ve diğer tüm parametreler dosya adlarıdır.

Yani işte nasıl oynandığı. Diyelim ki betiğinizin Patryk_scriptve bunun

Patryk_script USSR Russia Treaty1 Atlas2 Pravda3

Komut dosyası görür

$1 = USSR
$2 = Russia
$3 = Treaty1
$4 = Atlas2
$5 = Pravda3

İfadesi ostr="$1"değişken setleri ostriçin USSR. İlk shiftifade, konum parametrelerini aşağıdaki gibi değiştirir:

$1 = Russia
$2 = Treaty1
$3 = Atlas2
$4 = Pravda3

İfadesi nstr="$1"değişken setleri nstriçin Russia. İkinci shiftifade konumsal parametreleri aşağıdaki gibi değiştirir:

$1 = Treaty1
$2 = Atlas2
$3 = Pravda3

Sonra fordöngü değişiklikleri USSR( $ostrkadar) Russia( $nstrdosyalarda) Treaty1, Atlas2ve Pravda3.



Komut dosyasında birkaç sorun var.

  1. $ @ içindeki dosya için; yap

    Eğer komut dosyası olarak çağrılırsa

    Patryk_script SSCB Rusya Antlaşması1 "Dünya Atlası2" Pravda3

    görüyor

    1 $ = SSCB
    2 ABD Doları = Rusya
    3 ABD Doları = Antlaşma 1
    4 $ = Dünya Atlası2
    5 $ = Pravda3

    çünkü, $@alıntı değildir, uzay World Atlas2alıntı değildir ve fordöngü bu dört dosya vardır düşünür: Treaty1, World, Atlas2, ve Pravda3. Bu ya olmalı

    "$ @" içindeki dosya için; yap

    (bağımsız değişkenlerdeki herhangi bir özel karakteri alıntılamak için) veya basitçe

    dosya yapmak için

    (daha uzun sürüme eşdeğerdir).

  2. eval "sed 's /" $ ostr "/" $ nstr "/ g' $ dosyası"

    Bunun bir olmasına gerek yoktur evalve denetlenmeyen bir kullanıcı girişini bir kanala geçirmek evaltehlikeli olabilir. Örneğin, komut dosyası olarak çağrılırsa

    Patryk_script "'; rm *;'" Rusya Antlaşması1 Atlas2 Pravda3

    yürütecek rm *! Eğer komut dosyası, onu çağıran kullanıcınınkinden daha yüksek ayrıcalıklarla çalıştırılabilirse, bu büyük bir endişe kaynağıdır; örneğin, sudobir web arayüzünden çalıştırılabilir veya çalıştırılabilirse . Dizininizde, sadece kendiniz olarak kullanıyorsanız, muhtemelen o kadar önemli değildir. Ancak olarak değiştirilebilir

    sed "s / $ ostr / $ nstr / g" "$ dosyası"

    Bunun hala bazı riskleri var, ancak daha az şiddetli.

  3. if [ -f $file ], > $file.tmpVe mv $file.tmp $file olması gerektiği if [ -f "$file" ], > "$file.tmp"ve mv "$file.tmp" "$file"bunları boşluklar (veya diğer komik karakter) sahip olabilir dosya adları işlemek için sırasıyla. ( eval "sed …Komut ayrıca içinde boşluk bulunan dosya adlarını da düzenler.)


* shift isteğe bağlı bir argüman alır: kaç parametrenin kaydırılacağını belirten pozitif bir tamsayı. Varsayılan değer one ( 1) ' dır . Örneğin, shift 4neden $5 olmaya $1, $6olmaya $2vb, vb. ( Yeni Başlayanlar İçin Bash Kılavuzundaki örnekte yanlış olduğuna dikkat edin .)

ostr="$1"
nstr="$2"
shift 2

daha net olduğu düşünülebilir.



Son Not / Uyarı:

Windows Komut İstemi (toplu iş dosyası) dili SHIFT, aynı zamanda, shiftUnix mermilerindeki komutla aynı şeyi yapan ve insanların kafasını karıştırmasını önlemek için saklayacağım tek çarpıcı farkla bir komutu da destekler :

  • Benzeri bir komut SHIFT 4, “SHIFT komutuna geçersiz parametre” hata mesajı veren bir hatadır.
  • SHIFT /nNerede, n0 ile 8 arasında bir tam sayıdır, geçerlidir - ama vardiya değil kez . Bu, bir kez kaydırır ile başlayan n  argüman inci. Yani nedenler (beşinci argüman) olmak olmak tek başına 3'e kadar argümanları 0 bırakarak vb, vb.n SHIFT /4%5%4,  %6%5


2
shiftuygulanabilir için while, untilve hatta forbasit bir can çok daha ince şekilde dizileri işlemek için döngüler fordöngü. Sık sık forgibi döngüler içinde yararlı buluyorum ... set -f -- $args; IFS=$split; for arg do set -- $arg; shift "${number_of_fields_to_discard}" && fn_that_wants_split_arg "$arg"; done
mikeserv

3

En basit açıklama budur. Komutu düşünün:

/bin/command.sh SET location Cebu
/bin/command.sh SET location Cebu, Philippines 6014 

Sizlerin yardımı olmadan, shiftkonumun tam değerini çıkaramazsınız, çünkü bu değer keyfi olarak uzayabilir. İki kez kaydırdığınızda, args SETve locationkaldırılır ki:

x="$@"
echo "location = $x"

Bu konuda çok uzun bir bakış atın $@. Bu, sahip olduğu xboşluklara ve virgüllere rağmen tam konumu değişkene kaydedebileceği anlamına gelir . Özetle çağırırız shiftve sonra değişkenden kalanın değerini alırız $@.

GÜNCELLEŞTİRME Konseptini ve shiftolmadan faydasını gösteren çok kısa bir snippet ekliyorum , alanları doğru şekilde çıkarmak çok zor olurdu.

#!/bin/sh
#

[ $# -eq 0 ] && return 0

a=$1
shift
b=$@
echo $a
[ -n "$b" ] && echo $b

Açıklama : sonra shift, değişken b geri kalan eşyası, geçirilen alanların olursa olsun ya da vb ediliyor içerecektir [ -n "$b" ] && echo $bbiz sadece yazdırmak şekilde bir korumadır b bir içeriğe sahip olup olmadığını.


(1) Bu en basit açıklama değil. Bu var bir ilginç açısı. (2) Fakat bir grup tartışmayı (veya hepsini) birleştirmek istediğiniz sürece, "$*"yerine kullanma alışkanlığına girmek isteyebilirsiniz "$@". (3) Cevabınızı artıracağım, ama vermedim, çünkü “iki kez kaydır” diyorsunuz ama kodda göstermiyorsunuz. (4) Bir komut dosyasının komut satırından çok kelimeli bir değer alabilmesini istiyorsanız, kullanıcının tüm değeri tırnak içine almasını istemek daha iyi olur. … (Devam ediyor)
Scott

(Devam)… Bugün umursamıyor olabilirsiniz, ancak yaklaşımınız birden fazla boşluk ( Philippines   6014), ana boşluklar, izleyen boşluklar veya sekmelere sahip olmanıza izin vermiyor . (5) BTW, burada bash ile yaptığınız şeyi de yapabilirsiniz x="${*:3}".
Scott,

'*** çoklu alanlara izin vermiyor ***' yorumunun nereden geldiğini deşifre etmeye çalıştım, çünkü bu shiftkomutla ilgili değil . Belki $ @ ve $ * arasındaki ince farklara atıfta bulunabilirsiniz. Ancak, gerçek şu ki, yukarıdaki snippet'iniz, ne kadar boşluk bıraktığına ya da önündeki önemli noktalara bakılmaksızın konumu doğru şekilde çıkarabilir. Vardiyadan sonra, yer doğru yeri içerecektir.
typelogic

2

shift komut satırı argümanlarını FIFO kuyruğu olarak kabul edin, her çalıştırıldığında öğeyi popleft eder.

array = [a, b, c]
shift equivalent to
array.popleft
[b, c]
$1, $2,$3 can be interpreted as index of the array.
$# is the length of array
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.