パスが引数として指定された場合、「For」ループはリストを繰り返さない。

パスが引数として指定された場合、「For」ループはリストを繰り返さない。

練習中です。シェルスクリプトディレクトリをパラメータとして使用し、ディレクトリ内の各ファイルを繰り返して名前とサイズを出力する単純なスクリプトを作成しようとしています。

#!/bin/bash
# A practice shell script to try and display a list of file names
# and their sizes using the output of ls -l and cut.
declare -i index
export index=0
export name=""
export size=0

for file in $1 ; do
   index+=1
   name=`basename $file`
   size=`ls -l $file | cut -d " " -f 5`
   echo "$index: $name, size: $size bytes"
done

パラメータとして指定すると、./*ファイルに対してこれを行い、それがすべてです。ただし、上記のコードを編集して./*場所に配置すると、$1現在のディレクトリ内のすべてのファイルが機能して繰り返されます。

$1と同じでなければならないのになぜ同じことをしないのでしょうか./*

ベストアンサー1

その理由は、スクリプトを呼び出すシェルがワイルドカードパターンを拡張するためです。./* 今後スクリプトに渡されます。つまり、ワイルドカードパターンが一致すると、file1.txtスクリプトは次のようにfile4.txt呼び出されます。

./my_script.sh ./*

実際には、次のように解釈されます。

./my_script.sh file1.txt file2.txt file3.txt file4.txt

これはシェルスクリプトに表示されるパラメータです。

詳細については、以下を確認してください。シェル拡張順序に関する部分Bashリファレンスマニュアルにあります。

この問題を解決する方法は2つあります。

  1. 特定のディレクトリ内のすべてのファイルを常に繰り返したい場合は、そのディレクトリをパラメータとして渡してから繰り返します。
    for f in "$1"/*
    do
        # operations on "$f"
    done
    
  2. または、作業したいファイル名だけを渡すことが確実な場合は、次のように引数の完全なリストを繰り返します。
    for f in "$@"
    do
        # operations on "$f"
    done
    

確かに興味深い練習であるスクリプトにglobパターンを渡すことでこれを行うことができます(@ilkkachuの説明を参照)。 @fra-sanがコメントで述べたように、このアプローチには利点があります。スクリプトの使用に柔軟性を追加し、シェルのコマンドライン引数の制限をバイパスします(「引数リストが長すぎます」を参照)。 RAMはまだ長さを制限しますが、生成されたファイル名のリスト) - しかし、非常に注意が必要です。

  • パラメータを引用符(一重引用符または二重引用符)で囲むか、バックスラッシュでglob文字をエスケープしてシェルがglobを拡張するのを防ぐことができます。
    ./my_script.sh "./*"
    ./my_script.sh './*'
    ./my_script.sh ./\*
    
  • スクリプト内の位置パラメータを参照します。$1 引用しないこのようにして実際にシェルによって解釈されます(私たちがしばしば避けたいもの)。
  • 「解釈」には拡張(上記参照)だけでなく単語分割も含まれるため入力フィールド区切り記号 IFSトークン化が発生しないように空の文字列に置き換えます。
  • ループはfor次のとおりです
    IFS=
    for f in $1
    do
        # Operations on "$f"
    done
    

スクリプトに関するいくつかの一般的な注意:

  • 常にシェル変数を引用します。特にファイル名が含まれている場合、そうでなければスクリプトはスペースやその他のはるかにエキゾチックな文字を含むファイル名を見つけます。ファイル名には改行文字も許可されることを覚えておいてください!
  • ls次の出力を解析します。非常にイライラ同様の理由で。特定のファイルの属性を識別したい場合は、このstatツールがより良い選択です。たとえば、ファイルサイズを確認するには、次のようにします。
    size=$(stat --printf="%s" "$f")
    
  • これは尊敬される$( ... )コマンドの置き換えには、古い「バックティックスタイル」の代わりに「新しい」スタイルを使用してください` ... `
  • シェルスクリプトを確認することをお勧めしますshellcheckまた、これらの(および他の)可能なエラーソースから保護するために、多くのLinuxディストリビューションでスタンドアロンツールとしても使用できます。

おすすめ記事