シェルスクリプトでシフトを使用する目的は何ですか?


118

私はこのスクリプトに出くわしました:

#! /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

使用する行の意味は何shiftですか?スクリプトは少なくとも引数付きで使用する必要があると思います...?

回答:


141

shiftbash引数リストの先頭から引数を削除する組み込みの種類です。スクリプトに提供する3つの引数が利用可能であることを考えると$1$2$3、その後の呼び出しはshift 行います$2新しいです$1。A shift 2は2つずつシフトし、新しい$1ものを古いものにし$3ます。詳細については、こちらをご覧ください。


25
+1 回転ではなく、(引数の)配列からシフトしていることに注意してください。Shift / unshiftおよびpop / pushは、この一般的な種類の操作の一般的な名前です(たとえば、bash pushdおよびをpopd参照)。
goldilocks 14


@goldilocksは非常に真実です。「回転する」のではなく、「引数変数で引数を前方に移動する」など、別の単語を使用する方法を見つけたほうがよいでしょう。とにかく、本文の例とリファレンスへのリンクがそれを明確にしてくれることを願っています。
humanityANDpeace 14

スクリプトは言及しますがbash、質問はすべてのシェルに一般的であり、したがって、POSIX標準が優先さ:pubs.opengroup.org/onlinepubs/9699919799/utilities/...を
calandoa

32

ゴルディロックスはコメントとしておよび人類の参照が記述し、 shift(位置パラメータを再割り当て$1$2など、)それはとても$1古い値をとる$2$2の値をとる$3など、*   の古い値は$1破棄されます。($0変更されていません。)これを行う理由には次のものがあります。

  • 10番目の引数(ある場合)に簡単にアクセスできます。  $10動作しません$1-aと連結されていると解釈されます0 (したがって、のようなものが生成される可能性がありますHello0)。の後shift、10番目の引数はになり$9ます。(ただし、最近のほとんどのシェルでは、を使用できます${10}。)
  • 初心者のためのバッシュガイドが実証し、それは引数をループに使用することができます。IMNSHO、これは不器用です。forそのためにはるかに優れています。
  • サンプルスクリプトのように、いくつかの引数を除いて、すべての引数を同じ方法で簡単に処理できます。たとえば、スクリプトでは、 $1および$2はテキスト文字列であり$3、その他のすべてのパラメーターはファイル名です。

だから、ここでそれがどのように展開するかです。スクリプトが呼び出されPatryk_script、次のように呼び出されるとします

Patryk_script USSR Russia Treaty1 Atlas2 Pravda3

スクリプトは見る

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

このステートメントostr="$1"は、変数ostrをに設定しますUSSR。最初のshiftステートメントは、位置パラメーターを次のように変更します。

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

このステートメントnstr="$1"は、変数nstrをに設定しますRussia。2番目のshiftステートメントは、位置パラメーターを次のように変更します。

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

そして、forループ変化USSR$ostrへ)Russia$nstrファイル内)Treaty1Atlas2Pravda3



スクリプトにはいくつかの問題があります。

  1. $ @のファイル用。行う

    スクリプトが次のように呼び出された場合

    Patryk_scriptソ連ロシア条約1 "世界地図2" Pravda3

    見える

    $ 1 =ソ連
    2ドル=ロシア
    3ドル=条約1
    4ドル= World Atlas2
    5ドル= Pravda3

    ので、しかし、$@引用されていない、中にスペースがWorld Atlas2引用されず、forループが、それは4つのファイルを持っていると考えて:Treaty1WorldAtlas2、とPravda3。これは

    「$ @」のファイルの場合。行う

    (引数内の特殊文字を引用するため)または単に

    ファイルのために

    (これは長いバージョンと同等です)。

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

    これをにする必要はありません。evalチェックされていないユーザー入力をに渡すevalことは危険です。たとえば、スクリプトが次のように呼び出された場合

    Patryk_script "'; rm *;'"ロシア条約1 Atlas2 Pravda3

    実行されrm *ます!これは、スクリプトを呼び出すユーザーの特権よりも高い特権でスクリプトを実行できる場合、大きな懸念事項です。たとえば、sudoWebインターフェースを介して実行したり、Webインターフェースから呼び出したりできる場合。ディレクトリ内で自分自身として使用する場合は、おそらくそれほど重要ではありません。しかし、それはに変更することができます

    sed "s / $ ostr / $ nstr / g" "$ file"

    これにはまだいくつかのリスクがありますが、それほど深刻ではありません。

  3. if [ -f $file ]> $file.tmpおよびmv $file.tmp $fileif [ -f "$file" ]、スペース(またはその他の面白い文字)が含まれる可能性のあるファイル名を処理するために、それぞれ、およびである必要が> "$file.tmp"ありmv "$file.tmp" "$file"ます。(このeval "sed …コマンドは、スペースを含むファイル名もマングルします。)


* shiftオプションの引数を取ります。シフトするパラメーターの数を指定する正の整数です。デフォルトは1(1)です。たとえば、shift 4原因$5 になる$1$6になる$2、など。(初心者向けBash Guideの例は間違っていることに注意してください。)そして、あなたのスクリプトは、

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

より明確であると考えられるかもしれません。



終了ノート /警告:

Windowsコマンドプロンプト(バッチファイル)言語は、UnixシェルSHIFTshiftコマンドと基本的に同じことを行うコマンドをサポートしますが、1つの顕著な違いがあります。

  • のようなコマンドSHIFT 4はエラーであり、「SHIFTコマンドのパラメーターが無効です」というエラーメッセージが表示されます。
  • SHIFT /n、ここnで0〜8の整数は有効ですが、時間はシフトしません。それは、一度シフトで始まるN  番目の引数。したがって 、(5番目の引数)がになるなど、引数0から3だけが残ります。n SHIFT /4%5%4,  %6%5


2
shift適用することができるwhileuntilともfor単純なことができるよりもはるかに微妙な方法で配列を処理するためのループforループ。私は頻繁で、それが有用見つけるfor...様ループ 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

最も簡単な説明はこれです。次のコマンドを検討してください。

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

shiftあなたの助けがなければ、この値はcomplete意的に長くなる可能性があるため、場所の完全な値を抽出することはできません。2回シフトすると、引数SETおよびlocationは削除されて次のようになります。

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

$@物をじっと見つめてください。つまりx、スペースやカンマが含まれていても、完全な場所を変数に保存できます。したがって、要約すると、呼び出してshiftから、変数の残りの値を取得します$@

更新 概念と有用性を示す非常に短いスニペットを以下に追加します。shiftこれなしでは、フィールドを正しく抽出することは非常に困難です。

#!/bin/sh
#

[ $# -eq 0 ] && return 0

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

説明:の後shiftに、変数bには、スペースなどに関係なく、渡されるものの残りが含まれます。これ[ -n "$b" ] && echo $bは、bにコンテンツがある場合にのみbを出力する保護です。


(1)これは最も単純な説明ではありません。それはです面白い角度。(2)ただし、一連の引数(またはそれらすべて)を連結する限り、の代わりにを使用する習慣を身に付けたい場合があります。(3)私はあなたの答えに賛成するつもりでしたが、私はそうしませんでした。なぜなら、あなたは「シフトを2回」と言いますが、実際にはコードでそれを見せないからです。(4)スクリプトでコマンドラインから複数ワードの値を取得できるようにしたい場合は、値全体を引用符で囲むようにユーザーに要求することをお勧めします。…(続き)"$*""$@"
スコット

(続き)…今日は気にしないかもしれませんが、あなたのアプローチでは複数のスペース(Philippines   6014)、先頭のスペース、末尾のスペース、またはタブを使用できません。(5)ところで、あなたはここでbashでやっていることをでできるかもしれませんx="${*:3}"
スコット

あなたの '***が複数のスペースを許可しない***'コメントがshiftコマンドに関連していないため、ここから来ているところを解読しようとしました。$ @対$ *の微妙な違いについて言及しているのかもしれません。しかし、上記のスニペットは、末尾または先頭のスペースの数に関係なく、場所を正確に抽出できるという事実は依然として残っています。シフト後、場所には正しい場所が含まれます。
typelogic

2

shift コマンドライン引数をFIFOキューとして扱い、呼び出されるたびに要素をポップレフトします。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.